178 lines
4.3 KiB
Go
178 lines
4.3 KiB
Go
package config
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type Config struct {
|
|
AccountID string
|
|
POP3 POP3Config
|
|
SMTP SMTPConfig
|
|
Data DataConfig
|
|
HTTP HTTPConfig
|
|
}
|
|
|
|
type POP3Config struct {
|
|
Host string
|
|
Port int
|
|
Username string
|
|
Password string
|
|
UseTLS bool
|
|
TimeoutSeconds int
|
|
}
|
|
|
|
type SMTPConfig struct {
|
|
Host string
|
|
Port int
|
|
Username string
|
|
Password string
|
|
UseTLS bool
|
|
UseSTARTTLS bool
|
|
AuthRequired bool
|
|
AllowInsecureAuth bool
|
|
FromAddress string
|
|
FromName string
|
|
TimeoutSeconds int
|
|
}
|
|
|
|
type DataConfig struct {
|
|
DataDir string
|
|
DatabasePath string
|
|
RawMailDir string
|
|
AttachmentDir string
|
|
}
|
|
|
|
type HTTPConfig struct {
|
|
Host string
|
|
Port int
|
|
}
|
|
|
|
func (c POP3Config) Timeout() time.Duration {
|
|
return time.Duration(c.TimeoutSeconds) * time.Second
|
|
}
|
|
|
|
func (c SMTPConfig) Timeout() time.Duration {
|
|
return time.Duration(c.TimeoutSeconds) * time.Second
|
|
}
|
|
|
|
func Load(envPath string) (*Config, error) {
|
|
values := map[string]string{}
|
|
if envPath == "" {
|
|
envPath = ".env"
|
|
}
|
|
if err := readEnvFile(envPath, values); err != nil {
|
|
return nil, err
|
|
}
|
|
for _, item := range os.Environ() {
|
|
key, val, ok := strings.Cut(item, "=")
|
|
if ok {
|
|
values[key] = val
|
|
}
|
|
}
|
|
|
|
dataDir := getString(values, "DATA_DIR", "./data")
|
|
cfg := &Config{
|
|
AccountID: getString(values, "MAIL_ACCOUNT_ID", "default"),
|
|
POP3: POP3Config{
|
|
Host: getString(values, "POP3_HOST", ""),
|
|
Port: getInt(values, "POP3_PORT", 110),
|
|
Username: getString(values, "POP3_USERNAME", ""),
|
|
Password: getString(values, "POP3_PASSWORD", ""),
|
|
UseTLS: getBool(values, "POP3_USE_TLS", false),
|
|
TimeoutSeconds: getInt(values, "POP3_TIMEOUT_SECONDS", 30),
|
|
},
|
|
SMTP: SMTPConfig{
|
|
Host: getString(values, "SMTP_HOST", ""),
|
|
Port: getInt(values, "SMTP_PORT", 25),
|
|
Username: getString(values, "SMTP_USERNAME", ""),
|
|
Password: getString(values, "SMTP_PASSWORD", ""),
|
|
UseTLS: getBool(values, "SMTP_USE_TLS", false),
|
|
UseSTARTTLS: getBool(values, "SMTP_USE_STARTTLS", false),
|
|
AuthRequired: getBool(values, "SMTP_AUTH_REQUIRED", false),
|
|
AllowInsecureAuth: getBool(values, "SMTP_ALLOW_INSECURE_AUTH", false),
|
|
FromAddress: getString(values, "SMTP_FROM_ADDRESS", ""),
|
|
FromName: getString(values, "SMTP_FROM_NAME", ""),
|
|
TimeoutSeconds: getInt(values, "SMTP_TIMEOUT_SECONDS", 30),
|
|
},
|
|
Data: DataConfig{
|
|
DataDir: dataDir,
|
|
DatabasePath: getString(values, "DATABASE_PATH", filepath.Join(dataDir, "internal_mail.db")),
|
|
RawMailDir: getString(values, "RAW_MAIL_DIR", filepath.Join(dataDir, "raw")),
|
|
AttachmentDir: getString(values, "ATTACHMENT_DIR", filepath.Join(dataDir, "attachments")),
|
|
},
|
|
HTTP: HTTPConfig{
|
|
Host: getString(values, "HTTP_HOST", "127.0.0.1"),
|
|
Port: getInt(values, "HTTP_PORT", 8765),
|
|
},
|
|
}
|
|
return cfg, nil
|
|
}
|
|
|
|
func readEnvFile(path string, values map[string]string) error {
|
|
file, err := os.Open(path)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return nil
|
|
}
|
|
return fmt.Errorf("read env file: %w", err)
|
|
}
|
|
defer file.Close()
|
|
|
|
scanner := bufio.NewScanner(file)
|
|
for scanner.Scan() {
|
|
line := strings.TrimSpace(scanner.Text())
|
|
if line == "" || strings.HasPrefix(line, "#") {
|
|
continue
|
|
}
|
|
key, value, ok := strings.Cut(line, "=")
|
|
if !ok {
|
|
continue
|
|
}
|
|
key = strings.TrimSpace(key)
|
|
value = strings.TrimSpace(value)
|
|
value = strings.Trim(value, `"'`)
|
|
values[key] = value
|
|
}
|
|
return scanner.Err()
|
|
}
|
|
|
|
func getString(values map[string]string, key, def string) string {
|
|
if v, ok := values[key]; ok {
|
|
return v
|
|
}
|
|
return def
|
|
}
|
|
|
|
func getInt(values map[string]string, key string, def int) int {
|
|
v, ok := values[key]
|
|
if !ok || strings.TrimSpace(v) == "" {
|
|
return def
|
|
}
|
|
n, err := strconv.Atoi(v)
|
|
if err != nil {
|
|
return def
|
|
}
|
|
return n
|
|
}
|
|
|
|
func getBool(values map[string]string, key string, def bool) bool {
|
|
v, ok := values[key]
|
|
if !ok || strings.TrimSpace(v) == "" {
|
|
return def
|
|
}
|
|
switch strings.ToLower(strings.TrimSpace(v)) {
|
|
case "1", "true", "yes", "y", "on":
|
|
return true
|
|
case "0", "false", "no", "n", "off":
|
|
return false
|
|
default:
|
|
return def
|
|
}
|
|
}
|