refac: removed 7 direct and 12 indirect go modules for custom code instead

This commit is contained in:
Florian Sylvain committed 2026-10-21 23:13:46 +02:00
1 parent cfcf108488
commit c68e9a7858
73 files changed
+2455 -428

No files matched your search

+142
View File
@@ -0,0 +1,142 @@
package config
import (
"bufio"
"fmt"
"io"
"os"
"strings"
)
const (
dotenvExportPrefix = "export "
dotenvCommentMarker = "#"
dotenvInlineComment = " #"
dotenvByteOrderMark = "\xef\xbb\xbf"
dotenvMaxLineBytes = 1 << 20
dotenvAssignment = "="
dotenvDoubleQuote = '"'
dotenvSingleQuote = '\''
dotenvEscapeBackslash = '\\'
)
var dotenvDoubleQuoteEscapes = map[byte]byte{
'n': '\n',
'r': '\r',
't': '\t',
'"': '"',
'\\': '\\',
}
func LoadDotenv(path string) error {
file, err := os.Open(path)
if err != nil {
return err
}
defer func() { _ = file.Close() }()
values, err := ParseDotenv(file)
if err != nil {
return fmt.Errorf("%s: %w", path, err)
}
for name, value := range values {
if _, isSet := os.LookupEnv(name); !isSet {
if err := os.Setenv(name, value); err != nil {
return err
}
}
}
return nil
}
func ParseDotenv(reader io.Reader) (map[string]string, error) {
scanner := bufio.NewScanner(reader)
scanner.Buffer(make([]byte, 0, bufio.MaxScanTokenSize), dotenvMaxLineBytes)
values := map[string]string{}
for lineNumber := 1; scanner.Scan(); lineNumber++ {
line := strings.TrimSpace(strings.TrimPrefix(scanner.Text(), dotenvByteOrderMark))
if line == "" || strings.HasPrefix(line, dotenvCommentMarker) {
continue
}
name, value, err := parseDotenvAssignment(line)
if err != nil {
return nil, fmt.Errorf("line %d: %w", lineNumber, err)
}
values[name] = value
}
return values, scanner.Err()
}
func parseDotenvAssignment(line string) (string, string, error) {
line = strings.TrimSpace(strings.TrimPrefix(line, dotenvExportPrefix))
name, raw, found := strings.Cut(line, dotenvAssignment)
name = strings.TrimSpace(name)
if !found || !isDotenvName(name) {
return "", "", fmt.Errorf("expected NAME=value")
}
value, err := parseDotenvValue(strings.TrimSpace(raw))
return name, value, err
}
func isDotenvName(name string) bool {
if name == "" {
return false
}
for _, r := range name {
isLetter := r >= 'a' && r <= 'z' || r >= 'A' && r <= 'Z'
isDigit := r >= '0' && r <= '9'
if !isLetter && !isDigit && r != '_' && r != '.' {
return false
}
}
return true
}
func parseDotenvValue(raw string) (string, error) {
if raw == "" {
return "", nil
}
switch raw[0] {
case dotenvDoubleQuote:
return parseDoubleQuoted(raw)
case dotenvSingleQuote:
return parseSingleQuoted(raw)
}
if comment := strings.Index(raw, dotenvInlineComment); comment >= 0 {
raw = raw[:comment]
}
return strings.TrimSpace(raw), nil
}
func parseSingleQuoted(raw string) (string, error) {
end := strings.IndexByte(raw[1:], dotenvSingleQuote)
if end < 0 {
return "", fmt.Errorf("unterminated single quote")
}
return raw[1 : 1+end], nil
}
func parseDoubleQuoted(raw string) (string, error) {
var value strings.Builder
for i := 1; i < len(raw); i++ {
switch raw[i] {
case dotenvDoubleQuote:
return value.String(), nil
case dotenvEscapeBackslash:
i++
if i >= len(raw) {
return "", fmt.Errorf("unterminated double quote")
}
if unescaped, known := dotenvDoubleQuoteEscapes[raw[i]]; known {
value.WriteByte(unescaped)
} else {
value.WriteByte(dotenvEscapeBackslash)
value.WriteByte(raw[i])
}
default:
value.WriteByte(raw[i])
}
}
return "", fmt.Errorf("unterminated double quote")
}
+47
View File
@@ -0,0 +1,47 @@
package mailer
import (
"RenewCMS/internal/infrastructure/config"
"errors"
"net/smtp"
"strings"
)
type loginAuth struct {
username string
password string
}
func (a *loginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
if !server.TLS && !isLoopback(server.Name) {
return "", nil, errors.New("refusing to send credentials over an unencrypted connection")
}
return "LOGIN", nil, nil
}
func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
if !more {
return nil, nil
}
switch strings.ToLower(strings.TrimSpace(string(fromServer))) {
case "username:":
return []byte(a.username), nil
case "password:":
return []byte(a.password), nil
}
return nil, errors.New("unexpected server challenge")
}
func isLoopback(host string) bool {
return host == "localhost" || host == "127.0.0.1" || host == "::1"
}
func selectAuth(mechanisms string, settings config.SmtpSettings) smtp.Auth {
advertised := strings.Fields(strings.ToUpper(mechanisms))
for _, mechanism := range advertised {
if mechanism == "PLAIN" {
return smtp.PlainAuth("", settings.Login, settings.Password, settings.Host)
}
}
return &loginAuth{username: settings.Login, password: settings.Password}
}
@@ -4,22 +4,47 @@ import (
domain "RenewCMS/internal/domain/mail"
"RenewCMS/internal/infrastructure/config"
"bytes"
"crypto/tls"
"embed"
"html/template"
"log"
"gopkg.in/gomail.v2"
"time"
)
type MailRepository struct {
smtp config.SmtpSettings
transport transport
sender string
now func() time.Time
}
type Option func(*options)
type options struct {
tlsConfig *tls.Config
now func() time.Time
}
func WithTLSConfig(tlsConfig *tls.Config) Option {
return func(o *options) { o.tlsConfig = tlsConfig }
}
func WithClock(now func() time.Time) Option {
return func(o *options) { o.now = now }
}
//go:embed templates/*
var mailTemplateFiles embed.FS
func NewMailRepository(smtp config.SmtpSettings) *MailRepository {
return &MailRepository{smtp: smtp}
func NewMailRepository(smtp config.SmtpSettings, opts ...Option) *MailRepository {
configured := options{now: time.Now}
for _, opt := range opts {
opt(&configured)
}
return &MailRepository{
transport: newTransport(smtp, configured.tlsConfig),
sender: smtp.Sender,
now: configured.now,
}
}
const defaultSubject = "RenewCMS | Action required"
@@ -60,28 +85,23 @@ func (m MailRepository) Render(templateName string, data any) (string, string, e
}
func (m MailRepository) Send(receiverAddress string, templateName string, data any) error {
login := m.smtp.Login
sender := m.smtp.Sender
password := m.smtp.Password
smtpHost := m.smtp.Host
smtpPort := m.smtp.Port
d := gomail.NewDialer(smtpHost, smtpPort, login, password)
subject, body, err := m.Render(templateName, data)
if err != nil {
return err
}
msg := gomail.NewMessage()
msg.SetHeaders(map[string][]string{
"From": {"RenewCMS <" + sender + ">"},
"To": {receiverAddress},
"Subject": {subject},
})
msg.SetBody("text/html", body)
msg, err := newMessage(m.sender, receiverAddress, subject, body, m.now())
if err != nil {
log.Println("error when building email:", err)
return err
}
payload, err := msg.bytes()
if err != nil {
log.Println("error when encoding email:", err)
return err
}
if err := d.DialAndSend(msg); err != nil {
if err := m.transport.deliver(msg.from.Address, msg.to.Address, payload); err != nil {
log.Println("error when sending email:", err)
return err
}
+90
View File
@@ -0,0 +1,90 @@
package mailer
import (
"bytes"
"crypto/rand"
"encoding/hex"
"errors"
"fmt"
"mime"
"mime/quotedprintable"
"net/mail"
"strings"
"time"
)
const (
senderDisplayName = "RenewCMS"
messageIDBytes = 16
lineBreak = "\r\n"
)
type message struct {
from mail.Address
to mail.Address
subject string
body string
sentAt time.Time
}
func newMessage(sender string, receiver string, subject string, body string, sentAt time.Time) (message, error) {
from, err := mail.ParseAddress(sender)
if err != nil {
return message{}, fmt.Errorf("invalid sender address: %w", err)
}
to, err := mail.ParseAddress(receiver)
if err != nil {
return message{}, fmt.Errorf("invalid receiver address: %w", err)
}
if strings.ContainsAny(subject, "\r\n") {
return message{}, errors.New("subject contains a line break")
}
return message{
from: mail.Address{Name: senderDisplayName, Address: from.Address},
to: *to,
subject: subject,
body: body,
sentAt: sentAt,
}, nil
}
func (m message) messageID() (string, error) {
_, domain, _ := strings.Cut(m.from.Address, "@")
random := make([]byte, messageIDBytes)
if _, err := rand.Read(random); err != nil {
return "", err
}
return "<" + hex.EncodeToString(random) + "@" + domain + ">", nil
}
func (m message) bytes() ([]byte, error) {
id, err := m.messageID()
if err != nil {
return nil, err
}
var out bytes.Buffer
headers := []struct{ name, value string }{
{"From", m.from.String()},
{"To", m.to.String()},
{"Subject", mime.QEncoding.Encode("utf-8", m.subject)},
{"Date", m.sentAt.Format(time.RFC1123Z)},
{"Message-ID", id},
{"MIME-Version", "1.0"},
{"Content-Type", "text/html; charset=UTF-8"},
{"Content-Transfer-Encoding", "quoted-printable"},
}
for _, h := range headers {
out.WriteString(h.name + ": " + h.value + lineBreak)
}
out.WriteString(lineBreak)
encoder := quotedprintable.NewWriter(&out)
if _, err := encoder.Write([]byte(m.body)); err != nil {
return nil, err
}
if err := encoder.Close(); err != nil {
return nil, err
}
return out.Bytes(), nil
}
+116
View File
@@ -0,0 +1,116 @@
package mailer
import (
"RenewCMS/internal/infrastructure/config"
"crypto/tls"
"errors"
"net"
"net/smtp"
"strconv"
"time"
)
const (
implicitTLSPort = 465
dialTimeout = 10 * time.Second
sessionTimeout = 30 * time.Second
)
type transport struct {
settings config.SmtpSettings
tlsConfig *tls.Config
}
func newTransport(settings config.SmtpSettings, tlsConfig *tls.Config) transport {
if tlsConfig == nil {
tlsConfig = &tls.Config{}
}
tlsConfig = tlsConfig.Clone()
if tlsConfig.ServerName == "" {
tlsConfig.ServerName = settings.Host
}
if tlsConfig.MinVersion == 0 {
tlsConfig.MinVersion = tls.VersionTLS12
}
return transport{settings: settings, tlsConfig: tlsConfig}
}
func (t transport) deliver(sender string, receiver string, payload []byte) error {
client, err := t.connect()
if err != nil {
return err
}
defer func() { _ = client.Close() }()
if err := t.negotiate(client); err != nil {
return err
}
if err := client.Mail(sender); err != nil {
return err
}
if err := client.Rcpt(receiver); err != nil {
return err
}
if err := writeData(client, payload); err != nil {
return err
}
return client.Quit()
}
func (t transport) connect() (*smtp.Client, error) {
address := net.JoinHostPort(t.settings.Host, strconv.Itoa(t.settings.Port))
dialer := &net.Dialer{Timeout: dialTimeout}
connection, err := dialer.Dial("tcp", address)
if err != nil {
return nil, err
}
if err := connection.SetDeadline(time.Now().Add(sessionTimeout)); err != nil {
_ = connection.Close()
return nil, err
}
if t.settings.Port == implicitTLSPort {
connection = tls.Client(connection, t.tlsConfig)
}
client, err := smtp.NewClient(connection, t.settings.Host)
if err != nil {
_ = connection.Close()
return nil, err
}
return client, nil
}
func (t transport) negotiate(client *smtp.Client) error {
if t.settings.Port != implicitTLSPort {
if supported, _ := client.Extension("STARTTLS"); supported {
if err := client.StartTLS(t.tlsConfig); err != nil {
return err
}
}
}
return t.authenticate(client)
}
func (t transport) authenticate(client *smtp.Client) error {
if t.settings.Login == "" {
return nil
}
advertised, mechanisms := client.Extension("AUTH")
if !advertised {
return errors.New("smtp server does not support authentication")
}
return client.Auth(selectAuth(mechanisms, t.settings))
}
func writeData(client *smtp.Client, payload []byte) error {
writer, err := client.Data()
if err != nil {
return err
}
if _, err := writer.Write(payload); err != nil {
_ = writer.Close()
return err
}
return writer.Close()
}
@@ -11,8 +11,6 @@ import (
"slices"
"strings"
"time"
"golang.org/x/sync/singleflight"
)
const (
@@ -54,7 +52,7 @@ type ServeVariantUseCase struct {
maxSourceBytes int64
maxSourcePixels int64
renderSlots chan struct{}
flights singleflight.Group
flights flightGroup
tracker image.VariantUsageTracker
}
@@ -214,11 +212,11 @@ func (u *ServeVariantUseCase) lookupCached(variantName string, storedName string
func (u *ServeVariantUseCase) generated(ctx context.Context, variant image.Variant, storedName string, fingerprint string, previous image.VariantFile) (image.VariantFile, error) {
key := strings.Join([]string{variant.Name, fingerprint, storedName}, flightKeySep)
for {
file, err, _ := u.flights.Do(key, func() (any, error) {
file, err := u.flights.do(key, func() (image.VariantFile, error) {
return u.generate(ctx, variant, storedName, fingerprint, previous)
})
if err == nil {
return file.(image.VariantFile), nil
return file, nil
}
if ctx.Err() != nil || !errors.Is(err, context.Canceled) {
return image.VariantFile{}, err
@@ -0,0 +1,45 @@
package useCases
import (
"RenewCMS/internal/domain/image"
"errors"
"sync"
)
var errFlightAbandoned = errors.New("shared call ended without a result")
type flightCall struct {
done chan struct{}
value image.VariantFile
err error
}
type flightGroup struct {
mu sync.Mutex
calls map[string]*flightCall
}
func (g *flightGroup) do(key string, work func() (image.VariantFile, error)) (image.VariantFile, error) {
g.mu.Lock()
if existing, running := g.calls[key]; running {
g.mu.Unlock()
<-existing.done
return existing.value, existing.err
}
if g.calls == nil {
g.calls = map[string]*flightCall{}
}
call := &flightCall{done: make(chan struct{}), err: errFlightAbandoned}
g.calls[key] = call
g.mu.Unlock()
defer func() {
g.mu.Lock()
delete(g.calls, key)
g.mu.Unlock()
close(call.done)
}()
call.value, call.err = work()
return call.value, call.err
}