blob: 4d30784dc62b94dae6e812a25e6cb9d9084cf444 [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"
DTabidze52593392024-03-08 12:53:20 +04008 "html/template"
Giorgi Lekveishvilic89b9002023-12-21 13:09:26 +04009 "io"
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +040010 "log"
11 "net/http"
Giorgi Lekveishvili6b887be2023-06-22 14:38:19 +040012 "net/url"
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +040013
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +040014 "github.com/gorilla/mux"
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +040015
16 "github.com/giolekva/pcloud/core/installer"
17)
18
Giorgi Lekveishvili260a97d2023-12-08 15:04:16 +040019//go:embed create-account.html
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +040020var indexHtml []byte
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +040021
22//go:embed static/*
23var staticAssets embed.FS
24
25type Server struct {
Giorgi Lekveishvilic89b9002023-12-21 13:09:26 +040026 port int
27 repo installer.RepoIO
28 nsCreator installer.NamespaceCreator
29 createAccountAddr string
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +040030}
31
Giorgi Lekveishvilic89b9002023-12-21 13:09:26 +040032func NewServer(
33 port int,
34 repo installer.RepoIO,
35 nsCreator installer.NamespaceCreator,
36 createAccountAddr string,
37) *Server {
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +040038 return &Server{
39 port,
40 repo,
Giorgi Lekveishvili7fb28bf2023-06-24 19:51:16 +040041 nsCreator,
Giorgi Lekveishvilic89b9002023-12-21 13:09:26 +040042 createAccountAddr,
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +040043 }
44}
45
46func (s *Server) Start() {
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +040047 r := mux.NewRouter()
48 r.PathPrefix("/static/").Handler(http.FileServer(http.FS(staticAssets)))
49 r.Path("/").Methods("POST").HandlerFunc(s.createAdminAccount)
50 r.Path("/").Methods("GET").HandlerFunc(s.createAdminAccountForm)
51 http.Handle("/", r)
52 log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", s.port), nil))
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +040053}
54
DTabidze52593392024-03-08 12:53:20 +040055func (s *Server) createAdminAccountForm(w http.ResponseWriter, r *http.Request) {
56 renderRegistrationForm(w, formData{})
57}
58
59type formData struct {
60 UsernameErrors []string
61 PasswordErrors []string
62 Data createAccountReq
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +040063}
64
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +040065type createAccountReq struct {
Giorgi Lekveishvili260a97d2023-12-08 15:04:16 +040066 Username string `json:"username,omitempty"`
Giorgi Lekveishvilic89b9002023-12-21 13:09:26 +040067 Password string `json:"password,omitempty"`
Giorgi Lekveishvili260a97d2023-12-08 15:04:16 +040068 SecretToken string `json:"secretToken,omitempty"`
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +040069}
70
Giorgi Lekveishvilic89b9002023-12-21 13:09:26 +040071type apiCreateAccountReq struct {
72 Username string `json:"username,omitempty"`
73 Password string `json:"password,omitempty"`
74}
75
DTabidze52593392024-03-08 12:53:20 +040076type ValidationError struct {
77 Field string `json:"field"`
78 Message string `json:"message"`
79}
80
81type ErrorResponse struct {
82 Errors []ValidationError `json:"errors"`
83}
84
Giorgi Lekveishvili6b887be2023-06-22 14:38:19 +040085func getFormValue(v url.Values, name string) (string, error) {
86 items, ok := v[name]
87 if !ok || len(items) != 1 {
88 return "", fmt.Errorf("%s not found", name)
89 }
90 return items[0], nil
91}
92
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +040093func extractReq(r *http.Request) (createAccountReq, error) {
94 var req createAccountReq
Giorgi Lekveishvili6b887be2023-06-22 14:38:19 +040095 if err := func() error {
96 var err error
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +040097 if err = r.ParseForm(); err != nil {
Giorgi Lekveishvili6b887be2023-06-22 14:38:19 +040098 return err
99 }
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +0400100 if req.Username, err = getFormValue(r.PostForm, "username"); err != nil {
Giorgi Lekveishvili6b887be2023-06-22 14:38:19 +0400101 return err
102 }
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +0400103 if req.Password, err = getFormValue(r.PostForm, "password"); err != nil {
Giorgi Lekveishvili6b887be2023-06-22 14:38:19 +0400104 return err
105 }
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +0400106 if req.SecretToken, err = getFormValue(r.PostForm, "secret-token"); err != nil {
Giorgi Lekveishvili6b887be2023-06-22 14:38:19 +0400107 return err
108 }
109 return nil
110 }(); err != nil {
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +0400111 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
112 return createAccountReq{}, err
Giorgi Lekveishvili6b887be2023-06-22 14:38:19 +0400113 }
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400114 }
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +0400115 return req, nil
116}
117
DTabidze52593392024-03-08 12:53:20 +0400118func renderRegistrationForm(w http.ResponseWriter, data formData) {
119 tmpl, err := template.New("create-account").Parse(string(indexHtml))
120 if err != nil {
121 http.Error(w, err.Error(), http.StatusInternalServerError)
122 return
123 }
124 if err := tmpl.Execute(w, data); err != nil {
125 http.Error(w, err.Error(), http.StatusInternalServerError)
126 return
127 }
128}
129
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +0400130func (s *Server) createAdminAccount(w http.ResponseWriter, r *http.Request) {
Giorgi Lekveishvili39913692023-12-05 08:58:08 +0400131 req, err := extractReq(r)
132 if err != nil {
133 http.Error(w, err.Error(), http.StatusInternalServerError)
134 return
135 }
Giorgi Lekveishvilic89b9002023-12-21 13:09:26 +0400136 {
137 var buf bytes.Buffer
138 cr := apiCreateAccountReq{req.Username, req.Password}
139 if err := json.NewEncoder(&buf).Encode(cr); err != nil {
140 http.Error(w, err.Error(), http.StatusInternalServerError)
141 return
142 }
143 resp, err := http.Post(s.createAccountAddr, "application/json", &buf)
144 if err != nil {
Giorgi Lekveishvili83399052024-02-14 13:27:30 +0400145 var respBody bytes.Buffer
146 if _, err := io.Copy(&respBody, resp.Body); err != nil {
147 http.Error(w, err.Error(), http.StatusInternalServerError)
148 }
149 respStr := respBody.String()
150 log.Println(respStr)
151 http.Error(w, respStr, http.StatusInternalServerError)
Giorgi Lekveishvilic89b9002023-12-21 13:09:26 +0400152 return
153 }
Giorgi Lekveishvilic89b9002023-12-21 13:09:26 +0400154 if resp.StatusCode != http.StatusOK {
DTabidze52593392024-03-08 12:53:20 +0400155 var errResponse ErrorResponse
156 if err := json.NewDecoder(resp.Body).Decode(&errResponse); err != nil {
157 http.Error(w, "Error Decoding JSON", http.StatusInternalServerError)
158 return
159 }
160 var usernameErrors, passwordErrors []string
161 for _, err := range errResponse.Errors {
162 if err.Field == "username" {
163 usernameErrors = append(usernameErrors, err.Message)
164 }
165 if err.Field == "password" {
166 passwordErrors = append(passwordErrors, err.Message)
167 }
168 }
169 renderRegistrationForm(w, formData{
170 usernameErrors,
171 passwordErrors,
172 req,
173 })
Giorgi Lekveishvilic89b9002023-12-21 13:09:26 +0400174 return
175 }
176 }
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400177 {
Giorgi Lekveishvili7fb28bf2023-06-24 19:51:16 +0400178 config, err := s.repo.ReadConfig()
179 if err != nil {
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +0400180 http.Error(w, err.Error(), http.StatusInternalServerError)
181 return
Giorgi Lekveishvili7fb28bf2023-06-24 19:51:16 +0400182 }
183 if err != nil {
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +0400184 http.Error(w, err.Error(), http.StatusInternalServerError)
185 return
Giorgi Lekveishvili7fb28bf2023-06-24 19:51:16 +0400186 }
Giorgi Lekveishvili6e813182023-06-30 13:45:30 +0400187 nsGen := installer.NewPrefixGenerator(config.Values.NamespacePrefix)
188 suffixGen := installer.NewEmptySuffixGenerator()
Giorgi Lekveishvili7fb28bf2023-06-24 19:51:16 +0400189 appManager, err := installer.NewAppManager(s.repo, s.nsCreator)
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400190 if err != nil {
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +0400191 http.Error(w, err.Error(), http.StatusInternalServerError)
192 return
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400193 }
194 appsRepo := installer.NewInMemoryAppRepository(installer.CreateAllApps())
195 {
Giorgi Lekveishvili39913692023-12-05 08:58:08 +0400196 app, err := appsRepo.Find("headscale-user")
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400197 if err != nil {
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +0400198 http.Error(w, err.Error(), http.StatusInternalServerError)
199 return
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400200 }
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400201 if err := appManager.Install(app, nsGen, suffixGen, map[string]any{
Giorgi Lekveishvilie009a5d2024-01-05 14:10:11 +0400202 "username": req.Username,
203 "preAuthKey": map[string]any{
204 "enabled": false,
Giorgi Lekveishvili39913692023-12-05 08:58:08 +0400205 },
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400206 }); err != nil {
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +0400207 http.Error(w, err.Error(), http.StatusInternalServerError)
208 return
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400209 }
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400210 }
211 }
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +0400212 if _, err := w.Write([]byte("OK")); err != nil {
213 http.Error(w, err.Error(), http.StatusInternalServerError)
214 return
215 }
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400216}