update configuration
This commit is contained in:
parent
9fe6a6beb1
commit
fda0f59fd3
5 changed files with 144 additions and 48 deletions
|
|
@ -8,6 +8,11 @@ PORT=9000
|
|||
DB_DRIVER=sqlite
|
||||
DB_CONNECTION="./data/local.db?_pragma=foreign_keys(1)&_pragma=journal_mode(WAL)"
|
||||
|
||||
#Generate all secret values by running go run ./cmd/generate_secrets
|
||||
ID_ENCODING_ALPHABET=
|
||||
ID_ENCODING_PRIME=
|
||||
ID_ENCODING_INVERSE=
|
||||
ID_ENCODINDG_XOR_KEY=
|
||||
JWT_SECRET=
|
||||
# Go duration format
|
||||
JWT_EXPIRY=168h
|
||||
|
|
@ -19,6 +24,3 @@ MAILER_IMAP_PORT=
|
|||
MAILER_USERNAME=
|
||||
MAILER_PASSWORD=
|
||||
MAILER_EMAIL_FROM=
|
||||
MAILER_ENVELOPE_FROM=
|
||||
MAILER_SUPPORT_EMAIL=
|
||||
MAILER_SUPPORT_ENVELOPE_FROM=
|
||||
|
|
|
|||
61
cmd/generate_secrets/main.go
Normal file
61
cmd/generate_secrets/main.go
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 1. Generate Shuffled Alphabet
|
||||
// We start with standard base62
|
||||
chars := []rune("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")
|
||||
|
||||
// Fisher-Yates Shuffle
|
||||
// We use crypto/rand for the swap index to ensure high entropy
|
||||
for i := len(chars) - 1; i > 0; i-- {
|
||||
nBig, _ := rand.Int(rand.Reader, big.NewInt(int64(i+1)))
|
||||
j := int(nBig.Int64())
|
||||
chars[i], chars[j] = chars[j], chars[i]
|
||||
}
|
||||
alphabet := string(chars)
|
||||
|
||||
// 2. Generate Random 64-bit Prime
|
||||
// crypto/rand.Prime automatically generates a number of the given bit length
|
||||
// that is prime with high probability.
|
||||
primeBig, err := rand.Prime(rand.Reader, 64)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to generate prime: %v", err))
|
||||
}
|
||||
|
||||
// 3. Calculate Modular Inverse
|
||||
// (Prime * Inverse) % 2^64 == 1
|
||||
// We use 1 << 64 as the modulus
|
||||
modulus := new(big.Int).Lsh(big.NewInt(1), 64)
|
||||
inverseBig := new(big.Int).ModInverse(primeBig, modulus)
|
||||
|
||||
// 4. Generate Random 64-bit XOR Key
|
||||
// Just a random 64-bit integer
|
||||
xorKeyBig, _ := rand.Int(rand.Reader, modulus)
|
||||
|
||||
// 5. Generate Hex-encoded JWT Secret (32 bytes / 256 bits)
|
||||
jwtBytes := make([]byte, 32)
|
||||
if _, err := rand.Read(jwtBytes); err != nil {
|
||||
panic(fmt.Errorf("failed to generate jwt secret: %v", err))
|
||||
}
|
||||
jwtSecret := hex.EncodeToString(jwtBytes)
|
||||
|
||||
// --- OUTPUT ---
|
||||
fmt.Println("Here are your generated secrets. Copy these into your .env or config file.")
|
||||
fmt.Println(strings.Repeat("-", 60))
|
||||
|
||||
fmt.Printf("ALPHABET = \"%s\"\n", alphabet)
|
||||
fmt.Printf("PRIME = %s\n", primeBig.String())
|
||||
fmt.Printf("INVERSE = %s\n", inverseBig.String())
|
||||
fmt.Printf("XOR_KEY = %s\n", xorKeyBig.String())
|
||||
fmt.Printf("JWT_SECRET = \"%s\"\n", jwtSecret)
|
||||
|
||||
fmt.Println(strings.Repeat("-", 60))
|
||||
}
|
||||
|
|
@ -29,13 +29,26 @@ func New(cfg *config.Config) (*App, error) {
|
|||
return nil, fmt.Errorf("failed to run migrations: %w", err)
|
||||
}
|
||||
|
||||
emailClient := service.NewEmailClient(cfg.MailerSMTPHost, cfg.MailerSMTPPort, cfg.MailerIMAPHost, cfg.MailerIMAPPort, cfg.MailerUsername, cfg.MailerPassword)
|
||||
emailClient := service.NewEmailClient(
|
||||
cfg.MailerSMTPHost,
|
||||
cfg.MailerSMTPPort,
|
||||
cfg.MailerIMAPHost,
|
||||
cfg.MailerIMAPPort,
|
||||
cfg.MailerUsername,
|
||||
cfg.MailerPassword,
|
||||
)
|
||||
|
||||
userRepository := repository.NewUserRepository(database)
|
||||
|
||||
userService := service.NewUserService(userRepository)
|
||||
authService := service.NewAuthService(userRepository)
|
||||
emailService := service.NewEmailService(emailClient, cfg.MailerEmailFrom, cfg.MailerEnvelopeFrom, cfg.MailerSupportFrom, cfg.MailerSupportEnvelopeFrom, cfg.AppURL, cfg.AppName, cfg.AppEnv == "development")
|
||||
emailService := service.NewEmailService(
|
||||
emailClient,
|
||||
cfg.MailerEmailFrom,
|
||||
cfg.AppURL,
|
||||
cfg.AppName,
|
||||
cfg.AppEnv == "development",
|
||||
)
|
||||
|
||||
return &App{
|
||||
Cfg: cfg,
|
||||
|
|
|
|||
|
|
@ -20,19 +20,21 @@ type Config struct {
|
|||
DBDriver string
|
||||
DBConnection string
|
||||
|
||||
IDEncodingAlphabet string
|
||||
IDEncodingPrime uint64
|
||||
IDEncodingInverse uint64
|
||||
IDEncodingXorKey uint64
|
||||
|
||||
JWTSecret string
|
||||
JWTExpiry time.Duration
|
||||
|
||||
MailerSMTPHost string
|
||||
MailerSMTPPort int
|
||||
MailerIMAPHost string
|
||||
MailerIMAPPort int
|
||||
MailerUsername string
|
||||
MailerPassword string
|
||||
MailerEmailFrom string
|
||||
MailerEnvelopeFrom string
|
||||
MailerSupportFrom string
|
||||
MailerSupportEnvelopeFrom string
|
||||
MailerSMTPHost string
|
||||
MailerSMTPPort int
|
||||
MailerIMAPHost string
|
||||
MailerIMAPPort int
|
||||
MailerUsername string
|
||||
MailerPassword string
|
||||
MailerEmailFrom string
|
||||
}
|
||||
|
||||
func Load() *Config {
|
||||
|
|
@ -52,19 +54,21 @@ func Load() *Config {
|
|||
DBDriver: envString("DB_DRIVER", "sqlite"),
|
||||
DBConnection: envString("DB_CONNECTION", "./data/local.db?_pragma=foreign_keys(1)&_pragma=journal_mode(WAL)"),
|
||||
|
||||
IDEncodingAlphabet: envRequired("ID_ENCODING_ALPHABET"),
|
||||
IDEncodingPrime: envRequiredUint64("ID_ENCODING_PRIME"),
|
||||
IDEncodingInverse: envRequiredUint64("ID_ENCODING_INVERSE"),
|
||||
IDEncodingXorKey: envRequiredUint64("ID_ENCODING_XOR_KEY"),
|
||||
|
||||
JWTSecret: envRequired("JWT_SECRET"),
|
||||
JWTExpiry: envDuration("JWT_EXPIRY", 168*time.Hour), // 7 days default
|
||||
|
||||
MailerSMTPHost: envString("MAILER_SMTP_HOST", ""),
|
||||
MailerSMTPPort: envInt("MAILER_SMTP_PORT", 587),
|
||||
MailerIMAPHost: envString("MAILER_IMAP_HOST", ""),
|
||||
MailerIMAPPort: envInt("MAILER_IMAP_PORT", 993),
|
||||
MailerUsername: envString("MAILER_USERNAME", ""),
|
||||
MailerPassword: envString("MAILER_PASSWORD", ""),
|
||||
MailerEmailFrom: envString("MAILER_EMAIL_FROM", ""),
|
||||
MailerEnvelopeFrom: envString("MAILER_ENVELOPE_FROM", ""),
|
||||
MailerSupportFrom: envString("MAILER_SUPPORT_EMAIL_FROM", ""),
|
||||
MailerSupportEnvelopeFrom: envString("MAILER_SUPPORT_ENVELOPE_FROM", ""),
|
||||
MailerSMTPHost: envString("MAILER_SMTP_HOST", ""),
|
||||
MailerSMTPPort: envInt("MAILER_SMTP_PORT", 587),
|
||||
MailerIMAPHost: envString("MAILER_IMAP_HOST", ""),
|
||||
MailerIMAPPort: envInt("MAILER_IMAP_PORT", 993),
|
||||
MailerUsername: envString("MAILER_USERNAME", ""),
|
||||
MailerPassword: envString("MAILER_PASSWORD", ""),
|
||||
MailerEmailFrom: envString("MAILER_EMAIL_FROM", ""),
|
||||
}
|
||||
|
||||
return cfg
|
||||
|
|
@ -85,8 +89,7 @@ func (c *Config) Sanitized() *Config {
|
|||
Port: c.Port,
|
||||
AppTagline: c.AppTagline,
|
||||
|
||||
MailerEmailFrom: c.MailerEmailFrom,
|
||||
MailerEnvelopeFrom: c.MailerEnvelopeFrom,
|
||||
MailerEmailFrom: c.MailerEmailFrom,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -111,6 +114,20 @@ func envInt(key string, def int) int {
|
|||
return int(i)
|
||||
}
|
||||
|
||||
func envRequiredUint64(key string) uint64 {
|
||||
if value := os.Getenv(key); value != "" {
|
||||
i64, err := parseUint64(value)
|
||||
if err != nil {
|
||||
slog.Error("config invalid required uint64", "key", key, "value", value, "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
return i64
|
||||
}
|
||||
slog.Error("config required uint64 env var missing", "key", key)
|
||||
os.Exit(1)
|
||||
return 0
|
||||
}
|
||||
|
||||
func envDuration(key string, def time.Duration) time.Duration {
|
||||
value, ok := os.LookupEnv(key)
|
||||
if !ok || value == "" {
|
||||
|
|
@ -132,3 +149,7 @@ func envRequired(key string) string {
|
|||
os.Exit(1)
|
||||
return ""
|
||||
}
|
||||
|
||||
func parseUint64(s string) (uint64, error) {
|
||||
return strconv.ParseUint(s, 10, 64)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -126,26 +126,20 @@ func (nc *EmailClient) connectToIMAP() (*client.Client, error) {
|
|||
}
|
||||
|
||||
type EmailService struct {
|
||||
client *EmailClient
|
||||
fromEmail string
|
||||
fromEnvelope string
|
||||
supportEmail string
|
||||
supportEnvelope string
|
||||
isDev bool
|
||||
appURL string
|
||||
appName string
|
||||
client *EmailClient
|
||||
fromEmail string
|
||||
isDev bool
|
||||
appURL string
|
||||
appName string
|
||||
}
|
||||
|
||||
func NewEmailService(client *EmailClient, fromEmail, fromEnvelope, supportEmail, supportEnvelope, appURL, appName string, isDev bool) *EmailService {
|
||||
func NewEmailService(client *EmailClient, fromEmail, appURL, appName string, isDev bool) *EmailService {
|
||||
return &EmailService{
|
||||
client: client,
|
||||
fromEmail: fromEmail,
|
||||
fromEnvelope: fromEnvelope,
|
||||
supportEmail: supportEmail,
|
||||
supportEnvelope: supportEnvelope,
|
||||
isDev: isDev,
|
||||
appURL: appURL,
|
||||
appName: appName,
|
||||
client: client,
|
||||
fromEmail: fromEmail,
|
||||
isDev: isDev,
|
||||
appURL: appURL,
|
||||
appName: appName,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -159,10 +153,11 @@ func (s *EmailService) SendMagicLinkEmail(email, token, name string) error {
|
|||
}
|
||||
|
||||
params := &EmailParams{
|
||||
From: s.fromEmail,
|
||||
To: []string{email},
|
||||
Subject: subject,
|
||||
Text: body,
|
||||
From: s.fromString(),
|
||||
EnvelopeFrom: s.fromEmail,
|
||||
To: []string{email},
|
||||
Subject: subject,
|
||||
Text: body,
|
||||
}
|
||||
|
||||
_, err := s.client.SendWithContext(context.Background(), params)
|
||||
|
|
@ -172,6 +167,10 @@ func (s *EmailService) SendMagicLinkEmail(email, token, name string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
func (s *EmailService) fromString() string {
|
||||
return fmt.Sprintf("%s <%s>", s.appName, s.fromEmail)
|
||||
}
|
||||
|
||||
func magicLinkEmailTemplate(magicURL, appName string) (string, string) {
|
||||
subject := fmt.Sprintf("Sign in to %s", appName)
|
||||
body := fmt.Sprintf(`Click this link to sign in to your account:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue