| 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() |
| } |