func Domain(domain string) Option {
	return func(o *Options) error {
		o.Domain = domain
		return nil
	}
}
Domain sets the domain the server is running on.  Defaults to the current domain of the request
only (recommended).

This should be a hostname and not a URL. If set, the domain is treated as
being prefixed with a '.' - e.g. "example.com" becomes ".example.com" and
matches "www.example.com" and "secure.example.com".
package app
import (
	"crypto/rand"
	"crypto/sha512"
	"errors"
	"fmt"
	"github.com/OB1Company/filehive/fil"
	"github.com/OB1Company/filehive/repo"
	"github.com/gorilla/csrf"
	"github.com/gorilla/mux"
	"github.com/multiformats/go-multihash"
	"github.com/op/go-logging"
	"golang.org/x/crypto/pbkdf2"
	"net"
	"net/http"
	"os"
	"path"
	"regexp"
)
var log = logging.MustGetLogger("APP")
type FileHiveServer struct {
	db              *repo.Database
	walletBackend   fil.WalletBackend
	filecoinBackend fil.FilecoinBackend
	filecoinAddress string
	staticFileDir   string
	listener        net.Listener
	handler         http.Handler
	jwtKey          []byte
	domain          string
	mailgunKey      string
	mailDomain      string
	shutdown        chan struct{}
	testMode bool
	useSSL   bool
	sslCert  string
	sslKey   string
}
func NewServer(listener net.Listener, db *repo.Database, staticFileDir string, walletBackend fil.WalletBackend, filecoinBackend fil.FilecoinBackend, opts ...Option) (*FileHiveServer, error) {
	var options Options
	if err := options.Apply(opts...); err != nil {
		return nil, err
	}
	if listener == nil {
		return nil, errors.New("listener is nil")
	}
	if db == nil {
		return nil, errors.New("database is nil")
	}
	if staticFileDir == "" {
		return nil, errors.New("static file dir is empty string")
	}
	if options.TestMode {
		if _, ok := walletBackend.(*fil.PowergateWalletBackend); !ok {
			return nil, errors.New("MockWalletBackend must be used in testmode")
		}
		if _, ok := filecoinBackend.(*fil.PowergateBackend); !ok {
			return nil, errors.New("MockFilecoinBackend must be used in testmode")
		}
	}
	if options.JWTKey == nil {
		jwtKey := make([]byte, 32)
		rand.Read(jwtKey)
		options.JWTKey = jwtKey
	}
	if err := os.MkdirAll(path.Join(staticFileDir, "images"), os.ModePerm); err != nil {
		return nil, err
	}
	var (
		s = &FileHiveServer{
			db:              db,
			walletBackend:   walletBackend,
			filecoinBackend: filecoinBackend,
			filecoinAddress: options.FilecoinAddress,
			listener:        listener,
			staticFileDir:   staticFileDir,
			useSSL:          options.UseSSL,
			sslCert:         options.SSLCert,
			sslKey:          options.SSLKey,
			jwtKey:          options.JWTKey,
			domain:          options.Domain,
			mailgunKey:      options.MailgunKey,
			mailDomain:      options.MailDomain,
			shutdown:        make(chan struct{}),
		}
		topMux = http.NewServeMux()
	)
	r := s.newV1Router()
	csrfKey := make([]byte, 32)
	rand.Read(csrfKey)
	httpOnly := true
	secureToken := false
	if s.useSSL {
		httpOnly = false
		secureToken = true
	}
	csrfOpts := []csrf.Option{
		csrf.SameSite(csrf.SameSiteLaxMode),
		csrf.Secure(secureToken),
		csrf.HttpOnly(httpOnly),
		csrf.Path("/"),
	}
	if options.Domain != "" {
		csrfOpts = append(csrfOpts, csrf.Domain(options.Domain))
	}
	csrfMiddleware := csrf.Protect(csrfKey, csrfOpts...)
	r.Use(
		csrfMiddleware,
		s.setCSRFHeaderMiddleware,
		mux.CORSMethodMiddleware(r),
	)
	topMux.Handle("/api/v1/", r)
	s.handler = topMux
	return s, nil
}
func (s *FileHiveServer) Close() error {
	close(s.shutdown)
	return s.listener.Close()
}
func (s *FileHiveServer) Serve() error {
	var err error
	if s.useSSL {
		err = http.ServeTLS(s.listener, s.handler, s.sslCert, s.sslKey)
	} else {
		err = http.Serve(s.listener, s.handler)
	}
	if err == nil {
		log.Infof("FileHive server listening on %s\n", s.listener.Addr().String())
	}
	return err
}
func (s *FileHiveServer) newV1Router() *mux.Router {
	r := mux.NewRouter()
	r.Methods("OPTIONS")
	r.HandleFunc("/api/v1/user", s.handlePOSTUser).Methods("POST")
	r.HandleFunc("/api/v1/user/{emailOrID}", s.handleGETUser).Methods("GET")
	r.HandleFunc("/api/v1/login", s.handlePOSTLogin).Methods("POST")
	r.HandleFunc("/api/v1/image/{filename}", s.handleGETImage).Methods("GET")
	r.HandleFunc("/api/v1/dataset/{id}", s.handleGETDataset).Methods("GET")
	r.HandleFunc("/api/v1/latest", s.handleGETRecent).Methods("GET")
	r.HandleFunc("/api/v1/trending", s.handleGETTrending).Methods("GET")
	r.HandleFunc("/api/v1/search", s.handleGETSearch).Methods("GET")
	r.HandleFunc("/api/v1/confirm", s.handleGETConfirm).Methods("GET")
	r.HandleFunc("/api/v1/passwordreset", s.handleGETPasswordReset).Methods("GET")
	r.HandleFunc("/api/v1/passwordreset", s.handlePOSTPasswordReset).Methods("POST")
	r.HandleFunc("/api/v1/checkresetcode", s.handleGETCheckResetCode).Methods("GET")
	if s.testMode {
		r.HandleFunc("/api/v1/generatecoins", s.handlePOSTGenerateCoins).Methods("POST")
	}
	subRouter := r.PathPrefix("/api/v1").Subrouter()
	subRouter.Use(s.authenticationMiddleware)
	subRouter.HandleFunc("/logout", s.handlePOSTLogout).Methods("POST")
	subRouter.HandleFunc("/token/extend", s.handlePOSTTokenExtend).Methods("POST")
	subRouter.HandleFunc("/user", s.handleGETUser).Methods("GET")
	subRouter.HandleFunc("/user", s.handlePATCHUser).Methods("PATCH")
	subRouter.HandleFunc("/wallet/address", s.handleGETWalletAddress).Methods("GET")
	subRouter.HandleFunc("/wallet/balance", s.handleGETWalletBalance).Methods("GET")
	subRouter.HandleFunc("/wallet/send", s.handlePOSTWalletSend).Methods("POST")
	subRouter.HandleFunc("/wallet/transactions", s.handleGETWalletTransactions).Methods("GET")
	subRouter.HandleFunc("/dataset", s.handlePOSTDataset).Methods("POST")
	subRouter.HandleFunc("/delist/{id}", s.handleGETDelist).Methods("GET")
	subRouter.HandleFunc("/relist/{id}", s.handleGETRelist).Methods("GET")
	subRouter.HandleFunc("/delist", s.handlePOSTAdminDelist).Methods("POST")
	subRouter.HandleFunc("/relist", s.handlePOSTAdminRelist).Methods("POST")
	subRouter.HandleFunc("/dataset", s.handlePATCHDataset).Methods("PATCH")
	subRouter.HandleFunc("/datasets", s.handleGETDatasets).Methods("GET")
	subRouter.HandleFunc("/datasetdeal/{id}", s.handleGETDatasetDeal).Methods("GET")
	subRouter.HandleFunc("/purchase/{id}", s.handlePOSTPurchase).Methods("POST")
	subRouter.HandleFunc("/purchases", s.handleGETPurchases).Methods("GET")
	subRouter.HandleFunc("/purchased/{id}", s.handleGETPurchased).Methods("GET")
	subRouter.HandleFunc("/sales", s.handleGETSales).Methods("GET")
	subRouter.HandleFunc("/admin/sales", s.handleGETAdminSales).Methods("GET")
	subRouter.HandleFunc("/admin/datasets", s.handleGETAdminDatasets).Methods("GET")
	subRouter.HandleFunc("/download/{cid}", s.handleGETDatasetFile).Methods("GET")
	subRouter.HandleFunc("/users", s.handleGETUsers).Methods("GET")
	subRouter.HandleFunc("/users/disable", s.handlePOSTDisableUsers).Methods("POST")
	subRouter.HandleFunc("/users/enable", s.handlePOSTEnableUsers).Methods("POST")
	subRouter.HandleFunc("/users/makeadmin", s.handlePOSTMakeAdmin).Methods("POST")
	subRouter.HandleFunc("/users/makeuser", s.handlePOSTMakeUser).Methods("POST")
	return r
}
type Options struct {
	JWTKey          []byte
	Domain          string
	UseSSL          bool
	FilecoinAddress string
	SSLCert         string
	SSLKey          string
	TestMode        bool
	MailgunKey      string
	MailDomain      string
}
func (o *Options) Apply(opts ...Option) error {
	for i, opt := range opts {
		if err := opt(o); err != nil {
			return fmt.Errorf("option %d failed: %s", i, err)
		}
	}
	return nil
}
type Option func(*Options) error
func JWTKey(key []byte) Option {
	return func(o *Options) error {
		o.JWTKey = key
		return nil
	}
}