git.sr.ht/~pingoo/stdx@v0.0.0-20240218134121-094174641f6e/httpx/autocertpg/autocertpg.go (about)

     1  package autocertpg
     2  
     3  import (
     4  	"context"
     5  	"database/sql"
     6  	"fmt"
     7  
     8  	"git.sr.ht/~pingoo/stdx/crypto"
     9  	"git.sr.ht/~pingoo/stdx/db"
    10  	"git.sr.ht/~pingoo/stdx/log/slogx"
    11  	"golang.org/x/crypto/acme/autocert"
    12  )
    13  
    14  type Cache struct {
    15  	key []byte
    16  	db  db.DB
    17  }
    18  
    19  type cert struct {
    20  	Key           string `db:"key"`
    21  	EncryptedData []byte `db:"encrypted_data"`
    22  }
    23  
    24  func NewCache(db db.DB, key []byte) *Cache {
    25  	return &Cache{
    26  		db:  db,
    27  		key: key,
    28  	}
    29  }
    30  
    31  func (cache *Cache) Get(ctx context.Context, key string) (data []byte, err error) {
    32  	var cert cert
    33  	query := "SELECT * FROM certs WHERE key = $1"
    34  	logger := slogx.FromCtx(ctx)
    35  
    36  	err = cache.db.Get(ctx, &cert, query, key)
    37  	if err != nil {
    38  		logger.Warn("autocertpg.Get: getting cert from db", slogx.Err(err))
    39  		if err == sql.ErrNoRows {
    40  			err = autocert.ErrCacheMiss
    41  		}
    42  		return
    43  	}
    44  
    45  	data, err = crypto.Decrypt(cache.key, cert.EncryptedData, []byte(cert.Key))
    46  	if err != nil {
    47  		logger.Warn("autocertpg.Get: decrypting data", slogx.Err(err))
    48  		err = fmt.Errorf("autocertpg: decrypting data: %w", err)
    49  		return
    50  	}
    51  
    52  	return
    53  }
    54  
    55  func (cache *Cache) Put(ctx context.Context, key string, data []byte) (err error) {
    56  	query := `
    57  	INSERT INTO certs (key, encrypted_data)
    58  		VALUES ($1, $2)
    59  		ON CONFLICT (key)
    60  		DO UPDATE SET encrypted_data = $2
    61  	`
    62  	logger := slogx.FromCtx(ctx)
    63  
    64  	encryptedData, err := crypto.Encrypt(cache.key, data, []byte(key))
    65  	if err != nil {
    66  		logger.Warn("autocertpg.Put: encrypting data", slogx.Err(err))
    67  		err = fmt.Errorf("autocertpg: encrypting data: %w", err)
    68  		return
    69  	}
    70  
    71  	_, err = cache.db.Exec(ctx, query, key, encryptedData)
    72  	if err != nil {
    73  		logger.Warn("autocertpg.Put: inserting cert in DB", slogx.Err(err))
    74  		return
    75  	}
    76  
    77  	return
    78  }
    79  
    80  func (cache *Cache) Delete(ctx context.Context, key string) (err error) {
    81  	logger := slogx.FromCtx(ctx)
    82  	query := "DELETE FROM certs WHERE key = $1"
    83  
    84  	_, err = cache.db.Exec(ctx, query, key)
    85  	if err != nil {
    86  		logger.Warn("autocertpg.Delete: deleting cert", slogx.Err(err))
    87  		return
    88  	}
    89  
    90  	return
    91  }