package main import ( "context" "flag" "fmt" "log" "net" "net/http" "os" "git.chrishayward.xyz/x/users/proto" "git.chrishayward.xyz/x/users/server" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" "google.golang.org/grpc" "google.golang.org/grpc/reflection" "gopkg.in/yaml.v3" "gorm.io/driver/postgres" "gorm.io/driver/sqlite" "gorm.io/gorm" ) var configPath = flag.String("config", "./config/server.yaml", "--config=./config/server.yaml") type config struct { Port int `yaml:"port"` Secret string `yaml:"secret"` Gateway struct { URL string `yaml:"url"` Port int `yaml:"port"` } Database struct { Type string `yaml:"type"` File string `yaml:"file"` Host string `yaml:"host"` Port int `yaml:"port"` Name string `yaml:"name"` User string `yaml:"user"` Password string `yaml:"password"` } `yaml:"database"` } func main() { // Create the context. ctx := context.Background() ctx, cancel := context.WithCancel(ctx) defer cancel() // Parse the application flags. flag.Parse() // Read the config file. config := &config{} file, err := os.Open(*configPath) if err != nil { log.Fatalf("Failed to open config file: %v", err) } defer file.Close() if err := yaml.NewDecoder(file).Decode(&config); err != nil { log.Fatalf("Failed to read config file: %v", err) } // Create the server network listener. lis, err := net.Listen("tcp", fmt.Sprintf(":%d", config.Port)) if err != nil { log.Fatal(err) } // Initialize the database. var db *gorm.DB switch config.Database.Type { case "postgres": db, _ = gorm.Open(postgres.Open(fmt.Sprintf( "host=%s user=%s password=%s dbname=%s port=%d sslmode=disable", config.Database.Host, config.Database.User, config.Database.Password, config.Database.Name, config.Database.Port))) case "sqlite": fallthrough default: db, _ = gorm.Open(sqlite.Open(config.Database.File), &gorm.Config{}) } // Create the server. grpcServer := grpc.NewServer() proto.RegisterUsersServer(grpcServer, server.NewUsersServer(config.Secret, db)) reflection.Register(grpcServer) go grpcServer.Serve(lis) // Create the gateway client connection. conn, err := grpc.Dial(fmt.Sprintf("localhost:%d", config.Port), grpc.WithInsecure()) if err != nil { log.Fatalf("Failed to create client connection: %v", err) } defer conn.Close() // Create the gRPC gateway. rmux := runtime.NewServeMux() client := proto.NewUsersClient(conn) err = proto.RegisterUserHandlerClient(ctx, rmux, client) if err != nil { log.Fatalf("Failed to register client handler: %v", err) } // Create a standard HTTP router. mux := http.NewServeMux() // Mount the gRPC gateway. mux.Handle("/", rmux) // Create the gRPC OpenAPI UI. mux.HandleFunc("/swagger-ui/swagger.json", func(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "./proto/users.swagger.json") }) mux.Handle("/swagger-ui/", http.StripPrefix("/swagger-ui/", http.FileServer(http.Dir("./modules/swagger-ui/dist")))) // Start listening for requests. log.Println(fmt.Sprintf("Listening on:\n=>\tgRPC[::]:%d\n=>\tHTTP[::]:%d", config.Port, config.Gateway.Port)) err = http.ListenAndServe(fmt.Sprintf(":%d", config.Gateway.Port), mux) if err != nil { log.Fatalf("Failed to listen: %v", err) } }