github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/service/api/server/server.go (about)

     1  package server
     2  
     3  import (
     4  	"fmt"
     5  	"net/http"
     6  	"os"
     7  
     8  	"github.com/go-acme/lego/v3/providers/dns/cloudflare"
     9  	"github.com/gorilla/mux"
    10  	"github.com/tickoalcantara12/micro/v3/plugin"
    11  	pb "github.com/tickoalcantara12/micro/v3/proto/api"
    12  	"github.com/tickoalcantara12/micro/v3/service"
    13  	"github.com/tickoalcantara12/micro/v3/service/api"
    14  	"github.com/tickoalcantara12/micro/v3/service/api/auth"
    15  	"github.com/tickoalcantara12/micro/v3/service/api/handler"
    16  	aapi "github.com/tickoalcantara12/micro/v3/service/api/handler/api"
    17  	"github.com/tickoalcantara12/micro/v3/service/api/handler/event"
    18  	ahttp "github.com/tickoalcantara12/micro/v3/service/api/handler/http"
    19  	"github.com/tickoalcantara12/micro/v3/service/api/handler/rpc"
    20  	"github.com/tickoalcantara12/micro/v3/service/api/handler/web"
    21  	"github.com/tickoalcantara12/micro/v3/service/api/resolver"
    22  	"github.com/tickoalcantara12/micro/v3/service/api/resolver/grpc"
    23  	"github.com/tickoalcantara12/micro/v3/service/api/resolver/host"
    24  	"github.com/tickoalcantara12/micro/v3/service/api/resolver/path"
    25  	"github.com/tickoalcantara12/micro/v3/service/api/resolver/subdomain"
    26  	"github.com/tickoalcantara12/micro/v3/service/api/router"
    27  	regRouter "github.com/tickoalcantara12/micro/v3/service/api/router/registry"
    28  	httpapi "github.com/tickoalcantara12/micro/v3/service/api/server/http"
    29  	"github.com/tickoalcantara12/micro/v3/service/logger"
    30  	"github.com/tickoalcantara12/micro/v3/service/registry"
    31  	"github.com/tickoalcantara12/micro/v3/service/store"
    32  	"github.com/tickoalcantara12/micro/v3/util/acme"
    33  	"github.com/tickoalcantara12/micro/v3/util/acme/autocert"
    34  	"github.com/tickoalcantara12/micro/v3/util/acme/certmagic"
    35  	"github.com/tickoalcantara12/micro/v3/util/helper"
    36  	"github.com/tickoalcantara12/micro/v3/util/opentelemetry"
    37  	"github.com/tickoalcantara12/micro/v3/util/opentelemetry/jaeger"
    38  	"github.com/tickoalcantara12/micro/v3/util/sync/memory"
    39  	"github.com/tickoalcantara12/micro/v3/util/wrapper"
    40  	"github.com/opentracing/opentracing-go"
    41  	"github.com/urfave/cli/v2"
    42  )
    43  
    44  var (
    45  	Name                  = "api"
    46  	Address               = ":8080"
    47  	Handler               = "meta"
    48  	Resolver              = "micro"
    49  	APIPath               = "/"
    50  	ProxyPath             = "/{service:[a-zA-Z0-9]+}"
    51  	Namespace             = ""
    52  	ACMEProvider          = "autocert"
    53  	ACMEChallengeProvider = "cloudflare"
    54  	ACMECA                = acme.LetsEncryptProductionCA
    55  )
    56  
    57  var (
    58  	Flags = []cli.Flag{
    59  		&cli.StringFlag{
    60  			Name:    "address",
    61  			Usage:   "Set the api address e.g 0.0.0.0:8080",
    62  			EnvVars: []string{"MICRO_API_ADDRESS"},
    63  		},
    64  		&cli.StringFlag{
    65  			Name:    "handler",
    66  			Usage:   "Specify the request handler to be used for mapping HTTP requests to services; {api, event, http, rpc}",
    67  			EnvVars: []string{"MICRO_API_HANDLER"},
    68  		},
    69  		&cli.StringFlag{
    70  			Name:    "namespace",
    71  			Usage:   "Set the namespace used by the API e.g. com.example",
    72  			EnvVars: []string{"MICRO_API_NAMESPACE"},
    73  		},
    74  		&cli.StringFlag{
    75  			Name:    "resolver",
    76  			Usage:   "Set the hostname resolver used by the API {host, path, grpc}",
    77  			EnvVars: []string{"MICRO_API_RESOLVER"},
    78  		},
    79  		&cli.BoolFlag{
    80  			Name:    "enable_cors",
    81  			Usage:   "Enable CORS, allowing the API to be called by frontend applications",
    82  			EnvVars: []string{"MICRO_API_ENABLE_CORS"},
    83  			Value:   true,
    84  		},
    85  		&cli.BoolFlag{
    86  			Name:    "enable_acme",
    87  			Usage:   "Enables ACME support via Let's Encrypt. ACME hosts should also be specified.",
    88  			EnvVars: []string{"MICRO_API_ENABLE_ACME"},
    89  		},
    90  		&cli.StringFlag{
    91  			Name:    "acme_hosts",
    92  			Usage:   "Comma separated list of hostnames to manage ACME certs for",
    93  			EnvVars: []string{"MICRO_API_ACME_HOSTS"},
    94  		},
    95  		&cli.StringFlag{
    96  			Name:    "acme_provider",
    97  			Usage:   "The provider that will be used to communicate with Let's Encrypt. Valid options: autocert, certmagic",
    98  			EnvVars: []string{"MICRO_API_ACME_PROVIDER"},
    99  		},
   100  		&cli.BoolFlag{
   101  			Name:    "enable_tls",
   102  			Usage:   "Enable TLS support. Expects cert and key file to be specified",
   103  			EnvVars: []string{"MICRO_API_ENABLE_TLS"},
   104  		},
   105  		&cli.StringFlag{
   106  			Name:    "tls_cert_file",
   107  			Usage:   "Path to the TLS Certificate file",
   108  			EnvVars: []string{"MICRO_API_TLS_CERT_FILE"},
   109  		},
   110  		&cli.StringFlag{
   111  			Name:    "tls_key_file",
   112  			Usage:   "Path to the TLS Key file",
   113  			EnvVars: []string{"MICRO_API_TLS_KEY_FILE"},
   114  		},
   115  		&cli.StringFlag{
   116  			Name:    "tls_client_ca_file",
   117  			Usage:   "Path to the TLS CA file to verify clients against",
   118  			EnvVars: []string{"MICRO_API_TLS_CLIENT_CA_FILE"},
   119  		},
   120  	}
   121  )
   122  
   123  func Run(ctx *cli.Context) error {
   124  	if len(ctx.String("server_name")) > 0 {
   125  		Name = ctx.String("server_name")
   126  	}
   127  	if len(ctx.String("address")) > 0 {
   128  		Address = ctx.String("address")
   129  	}
   130  	if len(ctx.String("handler")) > 0 {
   131  		Handler = ctx.String("handler")
   132  	}
   133  	if len(ctx.String("resolver")) > 0 {
   134  		Resolver = ctx.String("resolver")
   135  	}
   136  	if len(ctx.String("acme_provider")) > 0 {
   137  		ACMEProvider = ctx.String("acme_provider")
   138  	}
   139  	if len(ctx.String("namespace")) > 0 {
   140  		Namespace = ctx.String("namespace")
   141  	}
   142  	if len(ctx.String("api_handler")) > 0 {
   143  		Handler = ctx.String("api_handler")
   144  	}
   145  	if len(ctx.String("api_address")) > 0 {
   146  		Address = ctx.String("api_address")
   147  	}
   148  	// initialise service
   149  	srv := service.New(service.Name(Name))
   150  
   151  	// Init API
   152  	var opts []api.Option
   153  
   154  	if ctx.Bool("enable_acme") {
   155  		hosts := helper.ACMEHosts(ctx)
   156  		opts = append(opts, api.EnableACME(true))
   157  		opts = append(opts, api.ACMEHosts(hosts...))
   158  		switch ACMEProvider {
   159  		case "autocert":
   160  			opts = append(opts, api.ACMEProvider(autocert.NewProvider()))
   161  		case "certmagic":
   162  			if ACMEChallengeProvider != "cloudflare" {
   163  				logger.Fatal("The only implemented DNS challenge provider is cloudflare")
   164  			}
   165  
   166  			apiToken := os.Getenv("CF_API_TOKEN")
   167  			if len(apiToken) == 0 {
   168  				logger.Fatal("env variables CF_API_TOKEN and CF_ACCOUNT_ID must be set")
   169  			}
   170  
   171  			storage := certmagic.NewStorage(
   172  				memory.NewSync(),
   173  				store.DefaultStore,
   174  			)
   175  
   176  			config := cloudflare.NewDefaultConfig()
   177  			config.AuthToken = apiToken
   178  			config.ZoneToken = apiToken
   179  			challengeProvider, err := cloudflare.NewDNSProviderConfig(config)
   180  			if err != nil {
   181  				logger.Fatal(err.Error())
   182  			}
   183  
   184  			opts = append(opts,
   185  				api.ACMEProvider(
   186  					certmagic.NewProvider(
   187  						acme.AcceptToS(true),
   188  						acme.CA(ACMECA),
   189  						acme.Cache(storage),
   190  						acme.ChallengeProvider(challengeProvider),
   191  						acme.OnDemand(false),
   192  					),
   193  				),
   194  			)
   195  		default:
   196  			logger.Fatalf("%s is not a valid ACME provider\n", ACMEProvider)
   197  		}
   198  	} else if ctx.Bool("enable_tls") {
   199  		config, err := helper.TLSConfig(ctx)
   200  		if err != nil {
   201  			fmt.Println(err.Error())
   202  			return err
   203  		}
   204  
   205  		opts = append(opts, api.EnableTLS(true))
   206  		opts = append(opts, api.TLSConfig(config))
   207  	}
   208  
   209  	if ctx.Bool("enable_cors") {
   210  		opts = append(opts, api.EnableCORS(true))
   211  	}
   212  
   213  	// create the router
   214  	var h http.Handler
   215  	r := mux.NewRouter()
   216  	h = r
   217  
   218  	// return version and list of services
   219  	r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
   220  		if r.Method == "OPTIONS" {
   221  			return
   222  		}
   223  
   224  		response := fmt.Sprintf(`{"version": "%s"}`, ctx.App.Version)
   225  		w.Write([]byte(response))
   226  	})
   227  
   228  	// strip favicon.ico
   229  	r.HandleFunc("/favicon.ico", func(w http.ResponseWriter, r *http.Request) {})
   230  
   231  	// resolver options
   232  	ropts := []resolver.Option{
   233  		resolver.WithServicePrefix(Namespace),
   234  		resolver.WithHandler(Handler),
   235  	}
   236  
   237  	// default resolver
   238  	rr := NewResolver(ropts...)
   239  
   240  	switch Resolver {
   241  	case "subdomain":
   242  		rr = subdomain.NewResolver(rr)
   243  	case "host":
   244  		rr = host.NewResolver(ropts...)
   245  	case "path":
   246  		rr = path.NewResolver(ropts...)
   247  	case "grpc":
   248  		rr = grpc.NewResolver(ropts...)
   249  	}
   250  
   251  	switch Handler {
   252  	case "rpc":
   253  		logger.Infof("Registering API RPC Handler at %s", APIPath)
   254  		rt := regRouter.NewRouter(
   255  			router.WithHandler(rpc.Handler),
   256  			router.WithResolver(rr),
   257  			router.WithRegistry(registry.DefaultRegistry),
   258  		)
   259  		rp := rpc.NewHandler(
   260  			handler.WithNamespace(Namespace),
   261  			handler.WithRouter(rt),
   262  			handler.WithClient(srv.Client()),
   263  		)
   264  		r.PathPrefix(APIPath).Handler(rp)
   265  	case "api":
   266  		logger.Infof("Registering API Request Handler at %s", APIPath)
   267  		rt := regRouter.NewRouter(
   268  			router.WithHandler(aapi.Handler),
   269  			router.WithResolver(rr),
   270  			router.WithRegistry(registry.DefaultRegistry),
   271  		)
   272  		ap := aapi.NewHandler(
   273  			handler.WithNamespace(Namespace),
   274  			handler.WithRouter(rt),
   275  			handler.WithClient(srv.Client()),
   276  		)
   277  		r.PathPrefix(APIPath).Handler(ap)
   278  	case "event":
   279  		logger.Infof("Registering API Event Handler at %s", APIPath)
   280  		rt := regRouter.NewRouter(
   281  			router.WithHandler(event.Handler),
   282  			router.WithResolver(rr),
   283  			router.WithRegistry(registry.DefaultRegistry),
   284  		)
   285  		ev := event.NewHandler(
   286  			handler.WithNamespace(Namespace),
   287  			handler.WithRouter(rt),
   288  			handler.WithClient(srv.Client()),
   289  		)
   290  		r.PathPrefix(APIPath).Handler(ev)
   291  	case "http":
   292  		logger.Infof("Registering API HTTP Handler at %s", ProxyPath)
   293  		rt := regRouter.NewRouter(
   294  			router.WithHandler(ahttp.Handler),
   295  			router.WithResolver(rr),
   296  			router.WithRegistry(registry.DefaultRegistry),
   297  		)
   298  		ht := ahttp.NewHandler(
   299  			handler.WithNamespace(Namespace),
   300  			handler.WithRouter(rt),
   301  			handler.WithClient(srv.Client()),
   302  		)
   303  		r.PathPrefix(ProxyPath).Handler(ht)
   304  	case "web":
   305  		logger.Infof("Registering API Web Handler at %s", APIPath)
   306  		rt := regRouter.NewRouter(
   307  			router.WithHandler(web.Handler),
   308  			router.WithResolver(rr),
   309  			router.WithRegistry(registry.DefaultRegistry),
   310  		)
   311  		w := web.NewHandler(
   312  			handler.WithNamespace(Namespace),
   313  			handler.WithRouter(rt),
   314  			handler.WithClient(srv.Client()),
   315  		)
   316  		r.PathPrefix(APIPath).Handler(w)
   317  	default:
   318  		logger.Infof("Registering API Default Handler at %s", APIPath)
   319  		rt := regRouter.NewRouter(
   320  			router.WithResolver(rr),
   321  			router.WithRegistry(registry.DefaultRegistry),
   322  		)
   323  		r.PathPrefix(APIPath).Handler(Meta(srv.Client(), rt, Namespace))
   324  	}
   325  
   326  	// register all the http handler plugins
   327  	for _, p := range plugin.Plugins() {
   328  		if v := p.Handler(); v != nil {
   329  			h = v(h)
   330  		}
   331  	}
   332  
   333  	reporterAddress := ctx.String("tracing_reporter_address")
   334  	if len(reporterAddress) == 0 {
   335  		reporterAddress = jaeger.DefaultReporterAddress
   336  	}
   337  	// Create a new Jaeger opentracer:
   338  	openTracer, traceCloser, err := jaeger.New(
   339  		opentelemetry.WithServiceName("API"),
   340  		opentelemetry.WithTraceReporterAddress(reporterAddress),
   341  	)
   342  	logger.Infof("Setting jaeger global tracer to %s", reporterAddress)
   343  	defer traceCloser.Close() // Make sure we flush any pending traces before shutdown:
   344  	if err != nil {
   345  		logger.Warnf("Unable to prepare a Jaeger tracer: %s", err)
   346  	} else {
   347  		// Set the global default opentracing tracer:
   348  		opentracing.SetGlobalTracer(openTracer)
   349  	}
   350  	opentelemetry.DefaultOpenTracer = openTracer
   351  
   352  	// append the opentelemetry wrapper
   353  	h = wrapper.HTTPWrapper(h)
   354  
   355  	// append the auth wrapper
   356  	h = auth.Wrapper(rr, Namespace)(h)
   357  
   358  	// create a new api server with wrappers
   359  	api := httpapi.NewServer(Address)
   360  	// initialise
   361  	api.Init(opts...)
   362  	// register the http handler
   363  	api.Handle("/", h)
   364  
   365  	// Start API
   366  	if err := api.Start(); err != nil {
   367  		logger.Fatal(err)
   368  	}
   369  
   370  	// register the rpc handler
   371  	pb.RegisterApiHandler(srv.Server(), &handler.APIHandler{})
   372  
   373  	// Run server
   374  	if err := srv.Run(); err != nil {
   375  		logger.Fatal(err)
   376  	}
   377  
   378  	// Stop API
   379  	if err := api.Stop(); err != nil {
   380  		logger.Fatal(err)
   381  	}
   382  
   383  	return nil
   384  }