feat: added fx, config, logger
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
// Copyright (c) 2025 Nikolai Papin
|
// Copyright (c) 2025 Nikolai Papin
|
||||||
//
|
//
|
||||||
// This file is part of the Auto Attendance app that looks for
|
// 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.
|
// browser.
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
@@ -19,6 +19,22 @@
|
|||||||
|
|
||||||
package main
|
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() {
|
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()
|
||||||
}
|
}
|
||||||
|
|||||||
21
src/go.mod
21
src/go.mod
@@ -1,3 +1,24 @@
|
|||||||
module git.weirdcat.su/weirdcat/auto-attendance
|
module git.weirdcat.su/weirdcat/auto-attendance
|
||||||
|
|
||||||
go 1.25.4
|
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
|
||||||
|
)
|
||||||
|
|||||||
35
src/go.sum
Normal file
35
src/go.sum
Normal file
@@ -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=
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
// Copyright (c) 2025 Nikolai Papin
|
// Copyright (c) 2025 Nikolai Papin
|
||||||
//
|
//
|
||||||
// This file is part of the Auto Attendance app that looks for
|
// 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.
|
// browser.
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
@@ -18,3 +18,132 @@
|
|||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package config
|
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
|
||||||
|
}
|
||||||
|
|||||||
24
src/internal/constants/app.go
Normal file
24
src/internal/constants/app.go
Normal file
@@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package constants
|
||||||
|
|
||||||
|
const (
|
||||||
|
AppName = "auto-attendance"
|
||||||
|
)
|
||||||
66
src/internal/logger/logger.go
Normal file
66
src/internal/logger/logger.go
Normal file
@@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
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}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user