Restructured project

- backend moved to backend directory
- added and initialized frontend with vue
- moved infrastructure files to infra directory
This commit is contained in:
2026-04-01 22:27:26 +03:00
parent 48ef7217eb
commit 9d845c8899
96 changed files with 1591 additions and 118 deletions
+91
View File
@@ -0,0 +1,91 @@
package config
import (
"errors"
"os"
"strings"
"github.com/joho/godotenv"
)
type Config struct {
RunMode RunMode
DebugMode bool
BotToken string
DBConnectionString string
OCRTokenPath string
TelegramApi string
APIPort string
APIHost string
APISecret string
OpenAPIEnabled bool
OpenAPIEndpoint string
}
func Load() (Config, error) {
_ = godotenv.Load()
var warnings []string
mode := os.Getenv("RUN_MODE")
debugMode := os.Getenv("DEBUG_MODE") == "true"
botToken := os.Getenv("BOT_TOKEN")
dbConnectionString := os.Getenv("DB_PATH")
ocrTokenPath := os.Getenv("GOOGLE_APPLICATION_CREDENTIALS")
apiPort := os.Getenv("API_PORT")
apiHost := os.Getenv("API_HOST")
apiSecret := os.Getenv("API_SECRET")
openAPIEnabled := os.Getenv("OPEN_API_ENABLED") == "true"
openAPIEndpoint := os.Getenv("OPEN_API_ENDPOINT")
runMode, err := ParseRunMode(mode)
if err != nil {
warnings = append(warnings, err.Error())
}
if runMode == Bot || runMode == Standalone {
if ocrTokenPath == "" {
warnings = append(warnings, "Missing required environment variable: GOOGLE_APPLICATION_CREDENTIALS")
}
if botToken == "" {
warnings = append(warnings, "Missing required environment variable: BOT_TOKEN")
}
}
if runMode == API || runMode == Standalone {
if apiSecret == "" {
warnings = append(warnings, "Missing required environment variable: API_SECRET")
}
if dbConnectionString == "" {
dbConnectionString = "sqlite://data/app.db"
}
if apiHost == "" {
apiHost = "localhost"
}
if apiPort == "" {
apiPort = "8000"
}
if openAPIEndpoint == "" {
openAPIEndpoint = "/docs"
}
}
if len(warnings) > 0 {
return Config{}, errors.New(strings.Join(warnings, "\n"))
}
return Config{
BotToken: botToken,
DBConnectionString: dbConnectionString,
OCRTokenPath: ocrTokenPath,
DebugMode: debugMode,
RunMode: runMode,
APIPort: apiPort,
APIHost: apiHost,
APISecret: apiSecret,
OpenAPIEnabled: openAPIEnabled,
OpenAPIEndpoint: openAPIEndpoint,
TelegramApi: "https://api.telegram.org",
}, nil
}
+106
View File
@@ -0,0 +1,106 @@
package config_test
import (
"FamilyHub/src/config"
"os"
"testing"
"github.com/stretchr/testify/assert"
)
type EnvFixture struct {
backup map[string]string
}
func NewEnvFixture() *EnvFixture {
env := make(map[string]string)
for _, e := range os.Environ() {
pair := split(e)
env[pair[0]] = pair[1]
}
// полностью чистое окружение
os.Clearenv()
return &EnvFixture{backup: env}
}
func (e *EnvFixture) Restore() {
os.Clearenv()
for key, val := range e.backup {
_ = os.Setenv(key, val)
}
}
func split(s string) [2]string {
var p [2]string
for i := 0; i < len(s); i++ {
if s[i] == '=' {
p[0] = s[:i]
p[1] = s[i+1:]
return p
}
}
return p
}
func MustSet(env map[string]string) {
for k, v := range env {
_ = os.Setenv(k, v)
}
}
func MustUnset(keys ...string) {
for _, k := range keys {
_ = os.Unsetenv(k)
}
}
func TestConfigLoad_Table(t *testing.T) {
env := NewEnvFixture()
defer env.Restore()
tests := []struct {
name string
env map[string]string
want config.Config
error bool
}{
{
name: "ok - values set",
env: map[string]string{"BOT_TOKEN": "abc", "DB_PATH": "db.sqlite"},
want: config.Config{BotToken: "abc", DBConnectionString: "db.sqlite"},
},
{
name: "fail - missing token",
env: map[string]string{}, // ничего нет
error: true,
},
{
name: "default DB path applied",
env: map[string]string{"BOT_TOKEN": "xyz"},
error: true,
},
}
for _, tc := range tests {
testCase := tc
t.Run(testCase.name, func(t *testing.T) {
t.Parallel()
os.Clearenv()
MustSet(testCase.env)
cfg, err := config.Load()
if testCase.error {
assert.Error(t, err)
return
}
assert.NoError(t, err)
assert.Equal(t, testCase.want.BotToken, cfg.BotToken)
assert.Equal(t, testCase.want.DBConnectionString, cfg.DBConnectionString)
})
}
}
+28
View File
@@ -0,0 +1,28 @@
package config
import (
"fmt"
"strings"
)
type RunMode string
const (
Bot RunMode = "bot"
API RunMode = "api"
Standalone RunMode = "standalone"
Unknown RunMode = "unknown"
)
func ParseRunMode(s string) (RunMode, error) {
switch strings.ToLower(s) {
case "bot":
return Bot, nil
case "api":
return API, nil
case "standalone":
return Standalone, nil
default:
return Unknown, fmt.Errorf("invalid run mode: %s", s)
}
}