From 0160eec423b1eb1c3a996066a6be5d4cd154d5fd Mon Sep 17 00:00:00 2001 From: Nikolai Papin Date: Mon, 24 Nov 2025 20:06:24 +0300 Subject: [PATCH] feat: added fx, config, logger --- src/cmd/main.go | 20 +++++- src/go.mod | 21 ++++++ src/go.sum | 35 +++++++++ src/internal/config/config.go | 131 +++++++++++++++++++++++++++++++++- src/internal/constants/app.go | 24 +++++++ src/internal/logger/logger.go | 66 +++++++++++++++++ 6 files changed, 294 insertions(+), 3 deletions(-) create mode 100644 src/go.sum create mode 100644 src/internal/constants/app.go create mode 100644 src/internal/logger/logger.go diff --git a/src/cmd/main.go b/src/cmd/main.go index 54bb12b..7f94658 100644 --- a/src/cmd/main.go +++ b/src/cmd/main.go @@ -1,7 +1,7 @@ // Copyright (c) 2025 Nikolai Papin // // This file is part of the Auto Attendance app that looks for -// self-attend QR-codes during lectures and opens their URLs in your +// self-attend QR-codes during lectures and opens their URLs in your // browser. // // This program is free software: you can redistribute it and/or modify @@ -19,6 +19,22 @@ package main +import ( + "git.weirdcat.su/weirdcat/auto-attendance/internal/config" + "git.weirdcat.su/weirdcat/auto-attendance/internal/logger" + "go.uber.org/fx" +) + func main() { - panic("not implemented") + app := fx.New( + fx.Provide( + config.NewConfig, + logger.NewLogger, + ), + fx.Invoke(func(log *logger.Logger) { + log.Info("Я футбольный мячик :w") + }), + ) + + app.Run() } diff --git a/src/go.mod b/src/go.mod index b2c49fa..ec0d5e2 100644 --- a/src/go.mod +++ b/src/go.mod @@ -1,3 +1,24 @@ module git.weirdcat.su/weirdcat/auto-attendance go 1.25.4 + +require github.com/spf13/viper v1.21.0 + +require ( + github.com/fsnotify/fsnotify v1.9.0 // indirect + github.com/go-viper/mapstructure/v2 v2.4.0 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/sagikazarmark/locafero v0.11.0 // indirect + github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect + github.com/spf13/afero v1.15.0 // indirect + github.com/spf13/cast v1.10.0 // indirect + github.com/spf13/pflag v1.0.10 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + go.uber.org/dig v1.19.0 // indirect + go.uber.org/fx v1.24.0 // indirect + go.uber.org/multierr v1.10.0 // indirect + go.uber.org/zap v1.26.0 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/sys v0.29.0 // indirect + golang.org/x/text v0.28.0 // indirect +) diff --git a/src/go.sum b/src/go.sum new file mode 100644 index 0000000..724bd2d --- /dev/null +++ b/src/go.sum @@ -0,0 +1,35 @@ +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= +github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc= +github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik= +github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= +github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= +github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= +github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= +github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= +github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= +github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +go.uber.org/dig v1.19.0 h1:BACLhebsYdpQ7IROQ1AGPjrXcP5dF80U3gKoFzbaq/4= +go.uber.org/dig v1.19.0/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= +go.uber.org/fx v1.24.0 h1:wE8mruvpg2kiiL1Vqd0CC+tr0/24XIB10Iwp2lLWzkg= +go.uber.org/fx v1.24.0/go.mod h1:AmDeGyS+ZARGKM4tlH4FY2Jr63VjbEDJHtqXTGP5hbo= +go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= +go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= +golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/src/internal/config/config.go b/src/internal/config/config.go index 8fdb1bb..fd48873 100644 --- a/src/internal/config/config.go +++ b/src/internal/config/config.go @@ -1,7 +1,7 @@ // Copyright (c) 2025 Nikolai Papin // // This file is part of the Auto Attendance app that looks for -// self-attend QR-codes during lectures and opens their URLs in your +// self-attend QR-codes during lectures and opens their URLs in your // browser. // // This program is free software: you can redistribute it and/or modify @@ -18,3 +18,132 @@ // along with this program. If not, see . package config + +import ( + "fmt" + "os" + "path/filepath" + + "git.weirdcat.su/weirdcat/auto-attendance/internal/constants" + "github.com/spf13/viper" +) + +type Config struct { + App AppConfig `mapstructure:"app"` + Communication CommunicationConfig `mapstructure:"communication"` + Telemetry TelemetryConfig `mapstructure:"telemetry"` + Logging LoggingConfig `mapstructure:"logging"` +} + +type AppConfig struct { + EnableAlarm bool `mapstructure:"enable_alarm"` + EnableLinkOpening bool `mapstructure:"enable_link_opening"` + UseAttendanceJounralApi bool `mapstructure:"use_attendance_journal_api"` + Browser string `mapstructure:"browser"` + EnableCheckingUpdates bool `mapstructure:"enable_checking_updates"` +} + +type CommunicationConfig struct { + QrUrl string `mapstructure:"self_approve_url"` + QrQueryToken string `mapstructure:"qr_query_token"` + ApiSelfApproveMethod string `mapstructure:"api_self_approve_method"` +} + +type TelemetryConfig struct { + EnableStatisticsCollection bool `mapstructure:"enable_anonymous_statistics_collection"` + EnableAnonymousErrorReports bool `mapstructure:"enable_anonymous_error_reports"` +} + +type LoggingConfig struct { + Level string `mapstructure:"level"` + Output string `mapstructure:"output"` +} + +func getDefaultConfig() Config { + return Config{ + App: AppConfig{ + EnableAlarm: false, + EnableLinkOpening: true, + UseAttendanceJounralApi: false, + Browser: "firefox", + EnableCheckingUpdates: true, + }, + Logging: LoggingConfig{ + Level: "info", + Output: "stdout", + }, + Telemetry: TelemetryConfig{ + EnableStatisticsCollection: true, + EnableAnonymousErrorReports: true, + }, + } +} + +func getConfigDir(appName string) (string, error) { + configDir, err := os.UserConfigDir() + if err != nil { + return "", fmt.Errorf("failed to get user config directory: %w", err) + } + + appConfigDir := filepath.Join(configDir, appName) + + if err := os.MkdirAll(appConfigDir, 0755); err != nil { + return "", fmt.Errorf("failed to create config directory: %w", err) + } + + return appConfigDir, nil +} + +func initializeViper(appName string) (*viper.Viper, string, error) { + configDir, err := getConfigDir(appName) + if err != nil { + return nil, "", err + } + + configFile := filepath.Join(configDir, appName+".toml") + + v := viper.New() + v.SetConfigFile(configFile) + v.SetConfigType("toml") + + defaults := getDefaultConfig() + + v.SetDefault("app.enable_alarm", defaults.App.EnableAlarm) + v.SetDefault("app.enable_link_opening", defaults.App.EnableLinkOpening) + v.SetDefault("app.use_attendance_journal_api", defaults.App.UseAttendanceJounralApi) + v.SetDefault("app.browser", defaults.App.Browser) + v.SetDefault("app.enable_checking_updates", defaults.App.EnableCheckingUpdates) + + v.SetDefault("logging.level", defaults.Logging.Level) + v.SetDefault("logging.output", defaults.Logging.Output) + + v.SetDefault("telemetry.enable_statistics_collection", defaults.Telemetry.EnableStatisticsCollection) + v.SetDefault("telemetry.enable_anonymous_error_reports", defaults.Telemetry.EnableAnonymousErrorReports) + + return v, configFile, nil +} + +func NewConfig() (*Config, error) { + v, configFile, err := initializeViper(constants.AppName) + if err != nil { + return nil, fmt.Errorf("failed to initialize viper: %w", err) + } + + if err := v.ReadInConfig(); err != nil { + if _, ok := err.(viper.ConfigFileNotFoundError); ok { + if err := v.SafeWriteConfig(); err != nil { + return nil, fmt.Errorf("failed to create config file: %w", err) + } + fmt.Printf("Created new config file at: %s\n", configFile) + } else { + return nil, fmt.Errorf("failed to read config file: %w", err) + } + } + + var config Config + if err := v.Unmarshal(&config); err != nil { + return nil, fmt.Errorf("failed to unmarshal config: %w", err) + } + + return &config, nil +} diff --git a/src/internal/constants/app.go b/src/internal/constants/app.go new file mode 100644 index 0000000..4e6eb7e --- /dev/null +++ b/src/internal/constants/app.go @@ -0,0 +1,24 @@ +// Copyright (c) 2025 Nikolai Papin +// +// This file is part of the Auto Attendance app that looks for +// self-attend QR-codes during lectures and opens their URLs in your +// browser. +// +// 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 . + +package constants + +const ( + AppName = "auto-attendance" +) diff --git a/src/internal/logger/logger.go b/src/internal/logger/logger.go new file mode 100644 index 0000000..3f2b7ea --- /dev/null +++ b/src/internal/logger/logger.go @@ -0,0 +1,66 @@ +// Copyright (c) 2025 Nikolai Papin +// +// This file is part of the Auto Attendance app that looks for +// self-attend QR-codes during lectures and opens their URLs in your +// browser. +// +// 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 . + +package logger + +import ( + "log/slog" + "os" + + "git.weirdcat.su/weirdcat/auto-attendance/internal/config" +) + +type Logger struct { + *slog.Logger + *config.Config +} + +func NewLogger(config *config.Config) *Logger { + var level slog.Level + switch config.Logging.Level { + case "debug": + level = slog.LevelDebug + case "info": + level = slog.LevelInfo + case "warn": + level = slog.LevelWarn + case "error": + level = slog.LevelError + default: + level = slog.LevelInfo + } + + var output *os.File + switch config.Logging.Output { + case "stderr": + output = os.Stderr + case "stdout": + output = os.Stdout + default: + output = os.Stdout + } + + handler := slog.NewTextHandler(output, &slog.HandlerOptions{ + Level: level, + }) + + logger := slog.New(handler) + + return &Logger{logger, config} +}