installer: use gorilla/mux in create account server
diff --git a/core/installer/welcome/welcome.go b/core/installer/welcome/welcome.go
index ac13f06..2517a6f 100644
--- a/core/installer/welcome/welcome.go
+++ b/core/installer/welcome/welcome.go
@@ -8,13 +8,13 @@
 	"net/http"
 	"net/url"
 
-	"github.com/labstack/echo/v4"
+	"github.com/gorilla/mux"
 
 	"github.com/giolekva/pcloud/core/installer"
 )
 
 //go:embed create-admin-account.html
-var indexHtml string
+var indexHtml []byte
 
 //go:embed static/*
 var staticAssets embed.FS
@@ -34,18 +34,22 @@
 }
 
 func (s *Server) Start() {
-	e := echo.New()
-	e.StaticFS("/static", echo.MustSubFS(staticAssets, "static"))
-	e.POST("/create-admin-account", s.createAdminAccount)
-	e.GET("/", s.createAdminAccountForm)
-	log.Fatal(e.Start(fmt.Sprintf(":%d", s.port)))
+	r := mux.NewRouter()
+	r.PathPrefix("/static/").Handler(http.FileServer(http.FS(staticAssets)))
+	r.Path("/").Methods("POST").HandlerFunc(s.createAdminAccount)
+	r.Path("/").Methods("GET").HandlerFunc(s.createAdminAccountForm)
+	http.Handle("/", r)
+	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", s.port), nil))
 }
 
-func (s *Server) createAdminAccountForm(c echo.Context) error {
-	return c.HTML(http.StatusOK, indexHtml)
+func (s *Server) createAdminAccountForm(w http.ResponseWriter, _ *http.Request) {
+	if _, err := w.Write(indexHtml); err != nil {
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
+	}
 }
 
-type createAdminAccountReq struct {
+type createAccountReq struct {
 	Username      string `json:"username,omitempty"`
 	Password      string `json:"password,omitempty"` // TODO(giolekva): actually use this
 	GandiAPIToken string `json:"gandiAPIToken,omitempty"`
@@ -60,72 +64,86 @@
 	return items[0], nil
 }
 
-func (s *Server) createAdminAccount(c echo.Context) error {
-	var req createAdminAccountReq
+func extractReq(r *http.Request) (createAccountReq, error) {
+	var req createAccountReq
 	if err := func() error {
 		var err error
-		f, err := c.FormParams()
-		if err != nil {
+		if err = r.ParseForm(); err != nil {
 			return err
 		}
-		if req.Username, err = getFormValue(f, "username"); err != nil {
+		if req.Username, err = getFormValue(r.PostForm, "username"); err != nil {
 			return err
 		}
-		if req.Password, err = getFormValue(f, "password"); err != nil {
+		if req.Password, err = getFormValue(r.PostForm, "password"); err != nil {
 			return err
 		}
-		if req.GandiAPIToken, err = getFormValue(f, "gandi-api-token"); err != nil {
+		if req.GandiAPIToken, err = getFormValue(r.PostForm, "gandi-api-token"); err != nil {
 			return err
 		}
-		if req.SecretToken, err = getFormValue(f, "secret-token"); err != nil {
+		if req.SecretToken, err = getFormValue(r.PostForm, "secret-token"); err != nil {
 			return err
 		}
 		return nil
 	}(); err != nil {
-		if err := json.NewDecoder(c.Request().Body).Decode(&req); err != nil {
-			return err
+		if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
+			return createAccountReq{}, err
 		}
 	}
+	return req, nil
+}
+
+func (s *Server) createAdminAccount(w http.ResponseWriter, r *http.Request) {
+	var req createAccountReq
 	// TODO(giolekva): accounts-ui create user req
 	{
 		config, err := s.repo.ReadConfig()
 		if err != nil {
-			return err
+			http.Error(w, err.Error(), http.StatusInternalServerError)
+			return
 		}
 		if err != nil {
-			return err
+			http.Error(w, err.Error(), http.StatusInternalServerError)
+			return
 		}
 		nsGen := installer.NewPrefixGenerator(config.Values.NamespacePrefix)
 		suffixGen := installer.NewEmptySuffixGenerator()
 		appManager, err := installer.NewAppManager(s.repo, s.nsCreator)
 		if err != nil {
-			return err
+			http.Error(w, err.Error(), http.StatusInternalServerError)
+			return
 		}
 		appsRepo := installer.NewInMemoryAppRepository(installer.CreateAllApps())
 		{
 			app, err := appsRepo.Find("certificate-issuer-private")
 			if err != nil {
-				return err
+				http.Error(w, err.Error(), http.StatusInternalServerError)
+				return
 			}
 			if err := appManager.Install(*app, nsGen, suffixGen, map[string]any{
 				"GandiAPIToken": req.GandiAPIToken,
 			}); err != nil {
-				return err
+				http.Error(w, err.Error(), http.StatusInternalServerError)
+				return
 			}
 		}
 		{
 			app, err := appsRepo.Find("tailscale-proxy")
 			if err != nil {
-				return err
+				http.Error(w, err.Error(), http.StatusInternalServerError)
+				return
 			}
 			if err := appManager.Install(*app, nsGen, suffixGen, map[string]any{
 				"Username": req.Username,
 				"IPSubnet": "10.1.0.0/24", // TODO(giolekva): this should be taken from the config generated during new env creation
 			}); err != nil {
-				return err
+				http.Error(w, err.Error(), http.StatusInternalServerError)
+				return
 			}
 			// TODO(giolekva): headscale accept routes
 		}
 	}
-	return c.String(http.StatusOK, "OK")
+	if _, err := w.Write([]byte("OK")); err != nil {
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
+	}
 }