blob: bcadea43f01db978ecb47b5cf50036300f008376 [file] [log] [blame]
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +04001package welcome
2
3import (
Giorgi Lekveishvilic89b9002023-12-21 13:09:26 +04004 "bytes"
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +04005 "embed"
6 "encoding/json"
7 "fmt"
Giorgi Lekveishvilic89b9002023-12-21 13:09:26 +04008 "io"
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +04009 "log"
10 "net/http"
Giorgi Lekveishvili6b887be2023-06-22 14:38:19 +040011 "net/url"
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +040012
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +040013 "github.com/gorilla/mux"
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +040014
15 "github.com/giolekva/pcloud/core/installer"
16)
17
Giorgi Lekveishvili260a97d2023-12-08 15:04:16 +040018//go:embed create-account.html
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +040019var indexHtml []byte
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +040020
21//go:embed static/*
22var staticAssets embed.FS
23
24type Server struct {
Giorgi Lekveishvilic89b9002023-12-21 13:09:26 +040025 port int
26 repo installer.RepoIO
27 nsCreator installer.NamespaceCreator
28 createAccountAddr string
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +040029}
30
Giorgi Lekveishvilic89b9002023-12-21 13:09:26 +040031func NewServer(
32 port int,
33 repo installer.RepoIO,
34 nsCreator installer.NamespaceCreator,
35 createAccountAddr string,
36) *Server {
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +040037 return &Server{
38 port,
39 repo,
Giorgi Lekveishvili7fb28bf2023-06-24 19:51:16 +040040 nsCreator,
Giorgi Lekveishvilic89b9002023-12-21 13:09:26 +040041 createAccountAddr,
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +040042 }
43}
44
45func (s *Server) Start() {
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +040046 r := mux.NewRouter()
47 r.PathPrefix("/static/").Handler(http.FileServer(http.FS(staticAssets)))
48 r.Path("/").Methods("POST").HandlerFunc(s.createAdminAccount)
49 r.Path("/").Methods("GET").HandlerFunc(s.createAdminAccountForm)
50 http.Handle("/", r)
51 log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", s.port), nil))
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +040052}
53
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +040054func (s *Server) createAdminAccountForm(w http.ResponseWriter, _ *http.Request) {
55 if _, err := w.Write(indexHtml); err != nil {
56 http.Error(w, err.Error(), http.StatusInternalServerError)
57 return
58 }
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +040059}
60
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +040061type createAccountReq struct {
Giorgi Lekveishvili260a97d2023-12-08 15:04:16 +040062 Username string `json:"username,omitempty"`
Giorgi Lekveishvilic89b9002023-12-21 13:09:26 +040063 Password string `json:"password,omitempty"`
Giorgi Lekveishvili260a97d2023-12-08 15:04:16 +040064 SecretToken string `json:"secretToken,omitempty"`
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +040065}
66
Giorgi Lekveishvilic89b9002023-12-21 13:09:26 +040067type apiCreateAccountReq struct {
68 Username string `json:"username,omitempty"`
69 Password string `json:"password,omitempty"`
70}
71
Giorgi Lekveishvili6b887be2023-06-22 14:38:19 +040072func getFormValue(v url.Values, name string) (string, error) {
73 items, ok := v[name]
74 if !ok || len(items) != 1 {
75 return "", fmt.Errorf("%s not found", name)
76 }
77 return items[0], nil
78}
79
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +040080func extractReq(r *http.Request) (createAccountReq, error) {
81 var req createAccountReq
Giorgi Lekveishvili6b887be2023-06-22 14:38:19 +040082 if err := func() error {
83 var err error
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +040084 if err = r.ParseForm(); err != nil {
Giorgi Lekveishvili6b887be2023-06-22 14:38:19 +040085 return err
86 }
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +040087 if req.Username, err = getFormValue(r.PostForm, "username"); err != nil {
Giorgi Lekveishvili6b887be2023-06-22 14:38:19 +040088 return err
89 }
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +040090 if req.Password, err = getFormValue(r.PostForm, "password"); err != nil {
Giorgi Lekveishvili6b887be2023-06-22 14:38:19 +040091 return err
92 }
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +040093 if req.SecretToken, err = getFormValue(r.PostForm, "secret-token"); err != nil {
Giorgi Lekveishvili6b887be2023-06-22 14:38:19 +040094 return err
95 }
96 return nil
97 }(); err != nil {
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +040098 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
99 return createAccountReq{}, err
Giorgi Lekveishvili6b887be2023-06-22 14:38:19 +0400100 }
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400101 }
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +0400102 return req, nil
103}
104
105func (s *Server) createAdminAccount(w http.ResponseWriter, r *http.Request) {
Giorgi Lekveishvili39913692023-12-05 08:58:08 +0400106 req, err := extractReq(r)
107 if err != nil {
108 http.Error(w, err.Error(), http.StatusInternalServerError)
109 return
110 }
Giorgi Lekveishvilic89b9002023-12-21 13:09:26 +0400111 {
112 var buf bytes.Buffer
113 cr := apiCreateAccountReq{req.Username, req.Password}
114 if err := json.NewEncoder(&buf).Encode(cr); err != nil {
115 http.Error(w, err.Error(), http.StatusInternalServerError)
116 return
117 }
118 resp, err := http.Post(s.createAccountAddr, "application/json", &buf)
119 if err != nil {
120 http.Error(w, err.Error(), http.StatusInternalServerError)
121 return
122 }
123 // TODO(gio): better handle status code and error message
124 if resp.StatusCode != http.StatusOK {
125 var e bytes.Buffer
126 io.Copy(&e, resp.Body)
127 http.Error(w, e.String(), http.StatusInternalServerError)
128 return
129 }
130 }
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400131 {
Giorgi Lekveishvili7fb28bf2023-06-24 19:51:16 +0400132 config, err := s.repo.ReadConfig()
133 if err != nil {
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +0400134 http.Error(w, err.Error(), http.StatusInternalServerError)
135 return
Giorgi Lekveishvili7fb28bf2023-06-24 19:51:16 +0400136 }
137 if err != nil {
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +0400138 http.Error(w, err.Error(), http.StatusInternalServerError)
139 return
Giorgi Lekveishvili7fb28bf2023-06-24 19:51:16 +0400140 }
Giorgi Lekveishvili6e813182023-06-30 13:45:30 +0400141 nsGen := installer.NewPrefixGenerator(config.Values.NamespacePrefix)
142 suffixGen := installer.NewEmptySuffixGenerator()
Giorgi Lekveishvili7fb28bf2023-06-24 19:51:16 +0400143 appManager, err := installer.NewAppManager(s.repo, s.nsCreator)
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400144 if err != nil {
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +0400145 http.Error(w, err.Error(), http.StatusInternalServerError)
146 return
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400147 }
148 appsRepo := installer.NewInMemoryAppRepository(installer.CreateAllApps())
149 {
Giorgi Lekveishvili39913692023-12-05 08:58:08 +0400150 app, err := appsRepo.Find("headscale-user")
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400151 if err != nil {
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +0400152 http.Error(w, err.Error(), http.StatusInternalServerError)
153 return
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400154 }
Giorgi Lekveishvili6e813182023-06-30 13:45:30 +0400155 if err := appManager.Install(*app, nsGen, suffixGen, map[string]any{
Giorgi Lekveishvilie009a5d2024-01-05 14:10:11 +0400156 "username": req.Username,
157 "preAuthKey": map[string]any{
158 "enabled": false,
Giorgi Lekveishvili39913692023-12-05 08:58:08 +0400159 },
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400160 }); err != nil {
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +0400161 http.Error(w, err.Error(), http.StatusInternalServerError)
162 return
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400163 }
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400164 }
165 }
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +0400166 if _, err := w.Write([]byte("OK")); err != nil {
167 http.Error(w, err.Error(), http.StatusInternalServerError)
168 return
169 }
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400170}