df/bff/main.go

163 lines
4.0 KiB
Go

package main
import (
"context"
"database/sql"
"embed"
"io/fs"
"log"
"net"
"net/http"
"time"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"github.com/itsscb/df/bff/api"
db "github.com/itsscb/df/bff/db/sqlc"
"github.com/itsscb/df/bff/gapi"
"github.com/itsscb/df/bff/pb"
"github.com/itsscb/df/bff/util"
_ "github.com/lib/pq"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
"google.golang.org/protobuf/encoding/protojson"
"github.com/golang-migrate/migrate/v4"
_ "github.com/golang-migrate/migrate/v4/database/postgres"
_ "github.com/golang-migrate/migrate/v4/source/file"
)
//go:embed doc/swagger
var swaggerFiles embed.FS
func main() {
var swaggerFS http.FileSystem
config, err := util.LoadConfig(".")
if err != nil {
log.Fatal("cannot load config:", err)
}
conn, err := sql.Open(config.DBDriver, config.DBSource)
if err != nil {
log.Fatalf("could not connect to DB: %s", err)
}
runDBMigration(config.MigrationURL, config.DBSource, config.MigrationRetries)
store := db.NewStore(conn)
if config.Environment == "development" {
subDir, err := fs.Sub(swaggerFiles, "doc/swagger")
if err != nil {
log.Fatalf("could not import swagger files")
}
swaggerFS = http.FS(subDir)
}
go runGatewayServer(config, store, swaggerFS)
runGRPCServer(config, store)
}
func runDBMigration(migrationURL string, dbSource string, retries int) {
migration, err := migrate.New(migrationURL, dbSource)
if err != nil {
log.Printf("could not create migrate instance. Retrying %d more times...\n", retries)
count := 1
var e error
for _ = range time.Tick(3 * time.Second) {
log.Printf("retry: %d/%d\n", count, retries)
migration, e = migrate.New(migrationURL, dbSource)
if e == nil || count >= retries {
break
}
count++
}
if e != nil {
log.Fatalf("cannot create new migrate instance for '%s': %s", migrationURL, err)
}
}
if err = migration.Up(); err != nil && err != migrate.ErrNoChange {
log.Fatal("failed to run migrate up")
}
log.Println("db migrated successfully")
}
func runGRPCServer(config util.Config, store db.Store) {
server, err := gapi.NewServer(config, store)
if err != nil {
log.Fatalf("could not start server: %s", err)
}
grpcServer := grpc.NewServer()
pb.RegisterDfServer(grpcServer, server)
reflection.Register(grpcServer)
listener, err := net.Listen("tcp", config.GRPCServerAddress)
if err != nil {
log.Fatal("cannot create gRPC server:", err)
}
log.Printf("start gRPC server at %s", listener.Addr().String())
err = grpcServer.Serve(listener)
if err != nil {
log.Fatal("cannot start gRPC server:", err)
}
}
func runGatewayServer(config util.Config, store db.Store, swaggerFS http.FileSystem) {
server, err := gapi.NewServer(config, store)
if err != nil {
log.Fatal("cannot create server")
}
jsonOption := runtime.WithMarshalerOption(runtime.MIMEWildcard, &runtime.JSONPb{
MarshalOptions: protojson.MarshalOptions{
UseProtoNames: true,
EmitUnpopulated: true,
},
UnmarshalOptions: protojson.UnmarshalOptions{
DiscardUnknown: true,
},
})
grpcMux := runtime.NewServeMux(jsonOption)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
err = pb.RegisterDfHandlerServer(ctx, grpcMux, server)
if err != nil {
log.Fatal("cannot register handler server")
}
mux := http.NewServeMux()
mux.Handle("/", grpcMux)
swaggerHandler := http.StripPrefix("/swagger/", http.FileServer(swaggerFS))
mux.Handle("/swagger/", swaggerHandler)
listener, err := net.Listen("tcp", config.HTTPServerAddress)
if err != nil {
log.Fatal("cannot create listener")
}
log.Printf("start HTTP gateway server at %s\n", listener.Addr().String())
err = http.Serve(listener, mux)
if err != nil {
log.Fatal("cannot start HTTP gateway server")
}
}
func runHTTPServer(config util.Config, store db.Store, swaggerFS http.FileSystem) {
server, err := api.NewServer(config, store, swaggerFS)
if err != nil {
log.Fatalf("could not start server: %s", err)
}
err = server.Start(config.HTTPServerAddress)
if err != nil {
log.Fatal("cannot start server:", err)
}
}