Switch to multipart upload of PDF to lob, move backend to using that.

This commit is contained in:
Eli Ribble 2026-04-17 02:22:05 +00:00
parent 2c0aa980e7
commit dc3cce0b8a
No known key found for this signature in database
5 changed files with 64 additions and 51 deletions

View file

@ -1,6 +1,7 @@
package main
import (
"bytes"
"context"
"flag"
"log"
@ -25,12 +26,21 @@ func main() {
os.Exit(1)
}
if *file == "" {
log.Printf("you must specify a file with -file")
os.Exit(1)
}
content, err := os.ReadFile(*file)
if err != nil {
log.Printf("read file: %v", err)
os.Exit(2)
}
client := lob.NewLob(key)
ctx := context.TODO()
req := lob.RequestLetterCreate{
To: *to,
From: *from,
File: *file,
File: bytes.NewReader(content),
Color: *color,
UseType: *use_type,
}

View file

@ -4,6 +4,7 @@ import (
"context"
"crypto/tls"
"fmt"
"io"
"os"
"github.com/rs/zerolog/log"
@ -102,11 +103,11 @@ type RequestAddressCreate struct {
Name string `json:"name"`
}
type RequestLetterCreate struct {
Color bool `json:"color"`
From string `json:"from"`
File string `json:"file"`
To string `json:"to"`
UseType string `json:"use_type"`
Color bool
From string
File io.Reader
To string
UseType string
}
func (l *Lob) AddressCreate(ctx context.Context, req RequestAddressCreate) (Address, error) {
@ -146,10 +147,24 @@ func (l *Lob) AddressList(ctx context.Context) ([]Address, error) {
func (l *Lob) LetterCreate(ctx context.Context, req RequestLetterCreate) (Letter, error) {
var result Letter
color_str := "false"
if req.Color {
color_str = "true"
}
resp, err := l.client.R().
SetBody(req).
SetMultipartField(
"file",
"content.pdf",
"application/pdf",
req.File,
).
SetMultipartFormData(map[string]string{
"color": color_str,
"from": req.From,
"to": req.To,
"use_type": req.UseType,
}).
SetContext(ctx).
SetContentType("application/json").
SetResult(&result).
SetPathParam("urlBase", l.urlBaseApi).
Post("https://{urlBase}/v1/letters")

View file

@ -4,17 +4,16 @@ import (
"bytes"
"context"
"fmt"
"io"
"time"
"github.com/Gleipnir-Technology/bob"
"github.com/Gleipnir-Technology/nidus-sync/config"
"github.com/Gleipnir-Technology/nidus-sync/db/models"
"github.com/Gleipnir-Technology/nidus-sync/lob"
"github.com/Gleipnir-Technology/nidus-sync/platform/file"
"github.com/Gleipnir-Technology/nidus-sync/platform/pdf"
"github.com/aarondl/opt/omit"
"github.com/google/uuid"
lob "github.com/lob/lob-go"
"github.com/rs/zerolog/log"
)
@ -71,7 +70,7 @@ func ComplianceSend(ctx context.Context, txn bob.Executor, row_id int32) error {
return fmt.Errorf("lob address for %d is null", organization.ID)
}
lob_address := organization.LobAddressID.MustGet()
letter, err := sendMail(ctx, lob_address, compliance_req.PublicID, site, address)
letter, err := sendMail(ctx, lob_address, compliance_req.PublicID, site, address, content)
if err != nil {
return fmt.Errorf("send mail: %w", err)
}
@ -83,7 +82,7 @@ func ComplianceSend(ctx context.Context, txn bob.Executor, row_id int32) error {
mailer, err := models.CommsMailers.Insert(&models.CommsMailerSetter{
AddressID: omit.From(address.ID),
Created: omit.From(time.Now()),
ExternalID: omit.From(letter.Id),
ExternalID: omit.From(letter.ID),
// ID
Recipient: omit.From(site.OwnerName),
UUID: omit.From(mailer_uuid),
@ -104,44 +103,33 @@ func ComplianceSend(ctx context.Context, txn bob.Executor, row_id int32) error {
return nil
}
func sendMail(ctx context.Context, org_address_id string, public_id string, site *models.Site, address *models.Address) (*lob.Letter, error) {
ctx_lob := context.WithValue(ctx, lob.ContextBasicAuth, lob.BasicAuth{UserName: config.LobAPIKey})
config := lob.NewConfiguration()
client := lob.NewAPIClient(config)
from_addr, _, err := client.AddressesApi.Get(ctx_lob, org_address_id).Execute()
if err != nil {
return nil, fmt.Errorf("get from address '%s': %w", org_address_id)
}
var to_addr = *lob.NewAddressEditable()
func sendMail(ctx context.Context, org_address_id string, public_id string, site *models.Site, address *models.Address, content []byte) (*lob.Letter, error) {
key := config.LobAPIKey
client := lob.NewLob(key)
line1 := address.Number + " " + address.Street
to_addr.SetAddressLine1(line1)
to_addr.SetAddressLine2("")
to_addr.SetAddressCity(address.Locality)
to_addr.SetAddressState(address.Region)
to_addr.SetAddressZip(address.PostalCode)
to_addr.SetAddressCountry(lob.COUNTRYEXTENDED_US)
addr_desc := fmt.Sprintf("site %d - %s", site.ID, site.OwnerName)
to_addr.SetDescription(addr_desc)
to_addr.SetName(site.OwnerName)
var use_type lob.LtrUseType = lob.LTRUSETYPE_OPERATIONAL
content_path := file.MailerPath(public_id)
var ltr = lob.NewLetterEditable(true, to_addr, from_addr, content_path, *lob.NewNullableLtrUseType(&use_type))
desc := fmt.Sprintf("Compliance request %s", public_id)
ltr.SetDescription(desc)
result, resp, err := client.LettersApi.Create(ctx_lob).LetterEditable(*ltr).Execute()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("read lob response")
addr_req := lob.RequestAddressCreate{
AddressLine1: line1,
AddressCity: address.Locality,
AddressState: address.Region,
AddressZip: address.PostalCode,
Name: site.OwnerName,
}
log.Debug().Str("status", resp.Status).Bytes("body", body).Msg("response from lob")
addr_to, err := client.AddressCreate(ctx, addr_req)
if err != nil {
return nil, fmt.Errorf("create letter: %w", err)
return nil, fmt.Errorf("create to addr: %w", err)
}
log.Info().Str("id", result.Id).Msg("Created Lob letter")
return result, nil
req := lob.RequestLetterCreate{
To: addr_to.ID,
From: org_address_id,
File: bytes.NewReader(content),
Color: true,
UseType: "operational",
}
letter, err := client.LetterCreate(ctx, req)
if err != nil {
return nil, fmt.Errorf("letter create: %w", err)
}
return &letter, nil
}

View file

@ -17,7 +17,7 @@ func GeneratePDF(ctx context.Context, path string) ([]byte, error) {
// capture pdf
var buf []byte
url := fmt.Sprintf("http://%s%s", config.Bind, path)
url := fmt.Sprintf("https://%s%s", config.DomainNidus, path)
log.Info().Str("url", url).Msg("Getting with headless chrome")
if err := chromedp.Run(chrome_ctx, printToPDF(url, &buf)); err != nil {
return nil, fmt.Errorf("print to pdf: %w", err)

View file

@ -60,8 +60,8 @@ func getMailer3(w http.ResponseWriter, r *http.Request) {
http.Error(w, "empty code", http.StatusBadRequest)
return
}
content, err := pdf.GeneratePDF(r.Context(), code)
path := fmt.Sprintf("/mailer/mode-3/%s/preview", code)
content, err := pdf.GeneratePDF(r.Context(), path)
if err != nil {
respondError(w, "generate pdf failure", err, http.StatusInternalServerError)
return