Setting un invoice pdf microservice
This commit is contained in:
		
							
								
								
									
										86
									
								
								cmd/micro/invoice/helpers.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								cmd/micro/invoice/helpers.go
									
									
									
									
									
										Normal 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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										145
									
								
								cmd/micro/invoice/invoice-handlers.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								cmd/micro/invoice/invoice-handlers.go
									
									
									
									
									
										Normal 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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										23
									
								
								cmd/micro/invoice/invoice-routes.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								cmd/micro/invoice/invoice-routes.go
									
									
									
									
									
										Normal 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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										79
									
								
								cmd/micro/invoice/invoice.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								cmd/micro/invoice/invoice.go
									
									
									
									
									
										Normal 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)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user