package main import ( "context" "flag" "github.com/golang/glog" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" pb "code.hackerspace.pl/hscloud/bgpwtf/invoice/proto" "code.hackerspace.pl/hscloud/go/mirko" ) var ( flagDatabasePath string flagInit bool flagDisablePKI bool ) type service struct { m *model } func (s *service) CreateInvoice(ctx context.Context, req *pb.CreateInvoiceRequest) (*pb.CreateInvoiceResponse, error) { if err := validateInvoiceData(req.InvoiceData); err != nil { return nil, status.Errorf(codes.InvalidArgument, "invoice data: %v", err) } uid, err := s.m.createInvoice(ctx, req.InvoiceData) if err != nil { if _, ok := status.FromError(err); ok { return nil, err } glog.Errorf("createInvoice(_, _): %v", err) return nil, status.Error(codes.Unavailable, "could not create invoice") } return &pb.CreateInvoiceResponse{ Uid: uid, }, nil } func (s *service) GetInvoice(ctx context.Context, req *pb.GetInvoiceRequest) (*pb.GetInvoiceResponse, error) { invoice, err := s.m.getInvoice(ctx, req.Uid) if err != nil { if _, ok := status.FromError(err); ok { return nil, err } glog.Errorf("getInvoice(_, %q): %v", req.Uid, err) return nil, status.Error(codes.Unavailable, "internal server error") } res := &pb.GetInvoiceResponse{ Invoice: invoice, } return res, nil } func newService(m *model) *service { return &service{ m: m, } } func (s *service) invoicePDF(ctx context.Context, uid, language string) ([]byte, error) { sealed, err := s.m.getSealedUid(ctx, uid) if err != nil { return nil, err } var rendered []byte if sealed != "" { // Invoice is sealed, return stored PDF. rendered, err = s.m.getRendered(ctx, uid) if err != nil { return nil, err } } else { // Invoice is proforma, render. invoice, err := s.m.getInvoice(ctx, uid) if err != nil { return nil, err } rendered, err = renderInvoicePDF(invoice, language) if err != nil { return nil, err } } return rendered, nil } func (s *service) RenderInvoice(req *pb.RenderInvoiceRequest, srv pb.Invoicer_RenderInvoiceServer) error { rendered, err := s.invoicePDF(srv.Context(), req.Uid, req.Language) if err != nil { if _, ok := status.FromError(err); ok { return err } glog.Errorf("invoicePDF(_, %q): %v", req.Uid, err) return status.Error(codes.Unavailable, "internal server error") } chunkSize := 16 * 1024 chunk := &pb.RenderInvoiceResponse{} for i := 0; i < len(rendered); i += chunkSize { if i+chunkSize > len(rendered) { chunk.Data = rendered[i:len(rendered)] } else { chunk.Data = rendered[i : i+chunkSize] } if err := srv.Send(chunk); err != nil { glog.Errorf("srv.Send: %v", err) return status.Error(codes.Unavailable, "stream broken") } } return nil } func (s *service) SealInvoice(ctx context.Context, req *pb.SealInvoiceRequest) (*pb.SealInvoiceResponse, error) { useProformaTime := false if req.DateSource == pb.SealInvoiceRequest_DATE_SOURCE_PROFORMA { useProformaTime = true } if err := s.m.sealInvoice(ctx, req.Uid, req.Language, useProformaTime); err != nil { if _, ok := status.FromError(err); ok { return nil, err } glog.Errorf("sealInvoice(_, %q): %v", req.Uid, err) return nil, status.Error(codes.Unavailable, "internal server error") } return &pb.SealInvoiceResponse{}, nil } func (s *service) GetInvoices(req *pb.GetInvoicesRequest, srv pb.Invoicer_GetInvoicesServer) error { return status.Error(codes.Unimplemented, "unimplemented") } func init() { flag.Set("logtostderr", "true") } func main() { flag.StringVar(&flagDatabasePath, "db_path", "./foo.db", "path to sqlite database") flag.BoolVar(&flagInit, "init_db", false, "init database and exit") flag.Parse() m, err := newModel(flagDatabasePath) if err != nil { glog.Exitf("newModel: %v", err) } if flagInit { glog.Exit(m.init()) } mi := mirko.New() if err := mi.Listen(); err != nil { glog.Exitf("Listen failed: %v", err) } s := newService(m) s.setupStatusz(mi) pb.RegisterInvoicerServer(mi.GRPC(), s) if err := mi.Serve(); err != nil { glog.Exitf("Serve failed: %v", err) } <-mi.Done() }