func (a API) Validate() error {
	var err error
	if a.port < 1 || a.port > 65535 {
		err = multierr.Append(err,
			errors.New("port must be more than 1 and less than 65535"),
		)
	}
	if !strings.HasPrefix(a.rootPath, "/") {
		err = multierr.Append(err,
			errors.New("root path must start with /"),
		)
	}
	if !strings.HasSuffix(a.rootPath, "/") {
		err = multierr.Append(err,
			errors.New("root path must end with /"),
		)
	}
	if len(strings.TrimSpace(a.traceHeader)) == 0 {
		err = multierr.Append(err,
			errors.New("trace header must not be empty"),
		)
	}
	if err != nil {
		return err
	}
	routesMap := make(map[string]string, len(a.routes)+1)
	routesMap["/health"] = "/health"
	for _, route := range a.routes {
		if route.Path == "" {
			return errors.New("route with empty path cannot be registered")
		}
		if !strings.HasPrefix(route.Path, "/") {
			return fmt.Errorf("route '%s' does not start with /", route.Path)
		}
		if route.IsMultipart && !strings.HasPrefix(route.Path, "/forms") {
			return fmt.Errorf("multipart/form-data route '%s' does not start with /forms", route.Path)
		}
		if route.Method == "" {
			return fmt.Errorf("route '%s' has an empty method", route.Path)
		}
		if route.Handler == nil {
			return fmt.Errorf("route '%s' has a nil handler", route.Path)
		}
		if _, ok := routesMap[route.Path]; ok {
			return fmt.Errorf("route '%s' is already registered", route.Path)
		}
		routesMap[route.Path] = route.Path
	}
	for _, middleware := range a.externalMiddlewares {
		if middleware.Handler == nil {
			return errors.New("a middleware has a nil handler")
		}
	}
	return nil
}
Validate validates the module properties.
package api
import (
	"context"
	"errors"
	"fmt"
	"net/http"
	"os"
	"sort"
	"strconv"
	"strings"
	"time"
	"github.com/alexliesenfeld/health"
	"github.com/gotenberg/gotenberg/v7/pkg/gotenberg"
	"github.com/gotenberg/gotenberg/v7/pkg/modules/gc"
	"github.com/labstack/echo/v4"
	flag "github.com/spf13/pflag"
	"go.uber.org/multierr"
	"go.uber.org/zap"
	"golang.org/x/net/http2"
)
func init() {
	gotenberg.MustRegisterModule(API{})
}
type API struct {
	port                      int
	readTimeout               time.Duration
	processTimeout            time.Duration
	writeTimeout              time.Duration
	rootPath                  string
	traceHeader               string
	disableHealthCheckLogging bool
	routes              []Route
	externalMiddlewares []Middleware
	healthChecks        []health.CheckerOption
	gcGraceDuration     time.Duration
	logger              *zap.Logger
	srv                 *echo.Echo
}
type Router interface {
	Routes() ([]Route, error)
}
type Route struct {
	Method string
	Path string
	IsMultipart bool
	DisableLogging bool
	Handler echo.HandlerFunc
}
type MiddlewareProvider interface {
	Middlewares() ([]Middleware, error)
}
type MiddlewareStack uint32
const (
	DefaultStack MiddlewareStack = iota
	PreRouterStack
	MultipartStack
)
type MiddlewarePriority uint32
const (
	VeryLowPriority MiddlewarePriority = iota
	LowPriority
	MediumPriority
	HighPriority
	VeryHighPriority
)
type Middleware struct {
	Stack MiddlewareStack
	Priority MiddlewarePriority
	Handler echo.MiddlewareFunc
}
type HealthChecker interface {
	Checks() ([]health.CheckerOption, error)
}
type GarbageCollectorGraceDurationIncrementer interface {
	AddGraceDuration() time.Duration
}
func (API) Descriptor() gotenberg.ModuleDescriptor {
	return gotenberg.ModuleDescriptor{
		ID: "api",
		FlagSet: func() *flag.FlagSet {
			fs := flag.NewFlagSet("api", flag.ExitOnError)
			fs.Int("api-port", 3000, "Set the port on which the API should listen")
			fs.String("api-port-from-env", "", "Set the environment variable with the port on which the API should listen - override the default port")
			fs.Duration("api-read-timeout", time.Duration(30)*time.Second, "Set the maximum duration allowed to read a complete request, including the body")
			fs.Duration("api-process-timeout", time.Duration(30)*time.Second, "Set the maximum duration allowed to process a request")
			fs.Duration("api-write-timeout", time.Duration(30)*time.Second, "Set the maximum duration before timing out writes of the response")
			fs.String("api-root-path", "/", "Set the root path of the API - for service discovery via URL paths")
			fs.String("api-trace-header", "Gotenberg-Trace", "Set the header name to use for identifying requests")
			fs.Bool("api-disable-health-check-logging", false, "Disable health check logging")
			return fs
		}(),
		New: func() gotenberg.Module { return new(API) },
	}
}
func (a *API) Provision(ctx *gotenberg.Context) error {
	flags := ctx.ParsedFlags()
	a.port = flags.MustInt("api-port")
	a.readTimeout = flags.MustDuration("api-read-timeout")
	a.processTimeout = flags.MustDuration("api-process-timeout")
	a.writeTimeout = flags.MustDuration("api-write-timeout")
	a.rootPath = flags.MustString("api-root-path")
	a.traceHeader = flags.MustString("api-trace-header")
	a.disableHealthCheckLogging = flags.MustBool("api-disable-health-check-logging")
	portEnvVar := flags.MustString("api-port-from-env")
	if portEnvVar != "" {
		val, ok := os.LookupEnv(portEnvVar)
		if !ok {
			return fmt.Errorf("environment variable '%s' does not exist", portEnvVar)
		}
		if val == "" {
			return fmt.Errorf("environment variable '%s' is empty", portEnvVar)
		}
		port, err := strconv.Atoi(val)
		if err != nil {
			return fmt.Errorf("get int value of environment variable '%s': %w", portEnvVar, err)
		}
		a.port = port
	}
	mods, err := ctx.Modules(new(Router))
	if err != nil {
		return fmt.Errorf("get routers: %w", err)
	}
	routers := make([]Router, len(mods))
	for i, router := range mods {
		routers[i] = router.(Router)
	}
	for _, router := range routers {
		routes, err := router.Routes()
		if err != nil {
			return fmt.Errorf("get routes: %w", err)
		}
		a.routes = append(a.routes, routes...)
	}
	mods, err = ctx.Modules(new(MiddlewareProvider))
	if err != nil {
		return fmt.Errorf("get middleware providers: %w", err)
	}
	middlewareProviders := make([]MiddlewareProvider, len(mods))
	for i, middlewareProvider := range mods {
		middlewareProviders[i] = middlewareProvider.(MiddlewareProvider)
	}
	for _, middlewareProvider := range middlewareProviders {
		middlewares, err := middlewareProvider.Middlewares()
		if err != nil {
			return fmt.Errorf("get middlewares: %w", err)
		}
		a.externalMiddlewares = append(a.externalMiddlewares, middlewares...)
	}
	sort.Slice(a.externalMiddlewares, func(i, j int) bool {
		return a.externalMiddlewares[i].Priority > a.externalMiddlewares[j].Priority
	})
	mods, err = ctx.Modules(new(HealthChecker))
	if err != nil {
		return fmt.Errorf("get health checkers: %w", err)
	}
	healthCheckers := make([]HealthChecker, len(mods))
	for i, healthChecker := range mods {
		healthCheckers[i] = healthChecker.(HealthChecker)
	}
	for _, healthChecker := range healthCheckers {
		checks, err := healthChecker.Checks()
		if err != nil {
			return fmt.Errorf("get health checks: %w", err)
		}
		a.healthChecks = append(a.healthChecks, checks...)
	}
	a.gcGraceDuration = a.readTimeout + a.processTimeout + a.writeTimeout
	mods, err = ctx.Modules(new(GarbageCollectorGraceDurationIncrementer))
	if err != nil {
		return fmt.Errorf("get garbage collector grace duration increments: %w", err)
	}
	for _, incrementer := range mods {
		a.gcGraceDuration += incrementer.(GarbageCollectorGraceDurationIncrementer).AddGraceDuration()
	}
	loggerProvider, err := ctx.Module(new(gotenberg.LoggerProvider))
	if err != nil {
		return fmt.Errorf("get logger provider: %w", err)
	}
	logger, err := loggerProvider.(gotenberg.LoggerProvider).Logger(a)
	if err != nil {
		return fmt.Errorf("get logger: %w", err)
	}
	a.logger = logger
	return nil
}