Setting un invoice pdf microservice

This commit is contained in:
2024-08-26 22:49:10 +02:00
parent 6547b6ac85
commit 08fb20d7be
10 changed files with 382 additions and 14 deletions

View File

@ -0,0 +1,86 @@
package main
import (
"encoding/json"
"errors"
"io"
"net/http"
"os"
)
type JSONResponse struct {
OK bool `json:"ok"`
Message string `json:"message,omitempty"`
}
func (app *application) readJSON(w http.ResponseWriter, r *http.Request, data interface{}) error {
maxBytes := 1048576
r.Body = http.MaxBytesReader(w, r.Body, int64(maxBytes))
dec := json.NewDecoder(r.Body)
err := dec.Decode(data)
if err != nil {
return err
}
// Make sure there is only one entry.
err = dec.Decode(&struct{}{})
if err != io.EOF {
return errors.New("body must only have a single JSON value")
}
return nil
}
// writeJSON writes arbitrary data out as JSON
func (app *application) writeJSON(
w http.ResponseWriter,
status int, data interface{},
headers ...http.Header,
) error {
out, err := json.MarshalIndent(data, "", "\t")
if err != nil {
return err
}
if len(headers) > 0 {
for k, v := range headers[0] {
w.Header()[k] = v
}
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
w.Write(out)
return nil
}
func (app *application) badRequest(w http.ResponseWriter, r *http.Request, err error) error {
var payload JSONResponse
payload.OK = false
payload.Message = err.Error()
out, err := json.MarshalIndent(payload, "", "\t")
if err != nil {
return err
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusBadGateway)
w.Write(out)
return nil
}
func (app *application) CreateDirIfNotExist(path string) error {
const mode = 0755
if _, err := os.Stat(path); os.IsNotExist(err) {
err := os.Mkdir(path, mode)
if err != nil {
app.errorLog.Println(err)
return err
}
}
return nil
}

View File

@ -0,0 +1,145 @@
package main
import (
"fmt"
"net/http"
"time"
"github.com/phpdave11/gofpdf"
"github.com/phpdave11/gofpdf/contrib/gofpdi"
)
type Order struct {
ID int `json:"id"`
Quantity int `json:"quantity"`
Amount int `json:"amount"`
Product string `json:"product"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Email string `json:"email"`
CreatedAt time.Time `json:"-"`
}
func (app *application) CreateAndSendInvoice(w http.ResponseWriter, r *http.Request) {
// receive json
var order Order
err := app.readJSON(w, r, &order)
if err != nil {
app.badRequest(w, r, err)
return
}
// generate a pdf invoice
// create mail
// send mail with attachment
// send response
var resp JSONResponse
resp.OK = true
resp.Message = fmt.Sprintf("Invoice %d.pdf created and sent to %s", order.ID, order.Email)
app.writeJSON(w, http.StatusCreated, resp)
}
func (app *application) createInvoicePDF(order Order) error {
pdf := gofpdf.New("P", "mm", "Letter", "")
pdf.SetMargins(10, 13, 10)
pdf.SetAutoPageBreak(true, 0)
importer := gofpdi.NewImporter()
t := importer.ImportPage(pdf, "./pdf-templates/invoice.pdf", q, "/MediaBox")
pdf.AddPage()
importer.UseImportedTemplate(pdf, t, 0, 0, 215.9, 0)
pdf.SetY(20)
// write info
pdf.SetY(50)
pdf.SetX(10)
pdf.SetFont("Times", "", 11)
pdf.CellFormat(
97,
8,
fmt.Sprintf("Attention: %s %s", order.FirstName, order.LastName),
"",
0,
"L",
false,
0,
"",
)
pdf.Ln(5)
pdf.CellFormat(
97,
8,
order.Email,
"",
0,
"L",
false,
0,
"",
)
pdf.Ln(5)
pdf.CellFormat(
97,
8,
order.CreatedAt.Format("2005-01-02"),
"",
0,
"L",
false,
0,
"",
)
pdf.SetX(58)
pdf.SetY(93)
pdf.CellFormat(
155,
8,
order.Product,
"",
0,
"L",
false,
0,
"",
)
pdf.SetX(166)
pdf.CellFormat(
20,
8,
fmt.Sprintf("%d", order.Quantity),
"",
0,
"C",
false,
0,
"",
)
pdf.SetX(185)
pdf.CellFormat(
155,
8,
fmt.Sprintf("€%.2f", float32(order.Amount/100.0)),
"",
0,
"R",
false,
0,
"",
)
invoicePath := fmt.Sprintf("./invoices/%d.pdf", order.ID)
err := pdf.OutputFileAndClose(invoicePath)
if err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,23 @@
package main
import (
"net/http"
"github.com/go-chi/chi/v5"
"github.com/go-chi/cors"
)
func (app *application) routes() http.Handler {
mux := chi.NewRouter()
mux.Use(cors.Handler(cors.Options{
AllowedOrigins: []string{"https://*", "http://*"},
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token"},
AllowCredentials: false,
MaxAge: 300,
}))
mux.Post("/invoice/create-and-send", app.CreateAndSendInvoice)
return mux
}

View File

@ -0,0 +1,79 @@
package main
import (
"flag"
"fmt"
"log"
"net/http"
"os"
"time"
)
const (
version = "1.0.0"
)
type config struct {
port int
smtp struct {
host string
port int
username string
password string
}
frontend string
}
type application struct {
config config
infoLog *log.Logger
errorLog *log.Logger
version string
}
func (app *application) serve() error {
srv := &http.Server{
Addr: fmt.Sprintf(":%d", app.config.port),
Handler: app.routes(),
IdleTimeout: 30 * time.Second,
ReadTimeout: 10 * time.Second,
ReadHeaderTimeout: 5 * time.Second,
WriteTimeout: 5 * time.Second,
}
app.infoLog.Printf(
"Starting invoice microservice on port %d",
app.config.port,
)
return srv.ListenAndServe()
}
func main() {
var cfg config
flag.IntVar(&cfg.port, "port", 5000, "Server port to listen on")
flag.StringVar(&cfg.smtp.host, "smtphost", "0.0.0.0", "smtp host")
flag.IntVar(&cfg.smtp.port, "smtpport", 1025, "smtp host")
flag.StringVar(&cfg.smtp.username, "smtpuser", "user", "smtp user")
flag.StringVar(&cfg.smtp.password, "smtppwd", "password", "smtp password")
flag.StringVar(&cfg.frontend, "frontend", "http://localhost:4000", "frontend address")
flag.Parse()
infoLog := log.New(os.Stdout, "INFO\t", log.Ldate|log.Ltime)
errorLog := log.New(os.Stdout, "ERROR\t", log.Ldate|log.Ltime|log.Lshortfile)
app := &application{
version: version,
config: cfg,
infoLog: infoLog,
errorLog: errorLog,
}
app.CreateDirIfNotExist("./invoices")
err := app.serve()
if err != nil {
log.Fatal(err)
}
}