blob: 5184a8e2ec8407c2ff70db964049e4aa4444ecb0 [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
Giorgi Lekveishvili83b72192024-03-11 18:36:14 +040022//go:embed create-account-success.html
23var successHtml []byte
24
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +040025//go:embed static/*
26var staticAssets embed.FS
27
28type Server struct {
Giorgi Lekveishvilic89b9002023-12-21 13:09:26 +040029 port int
30 repo installer.RepoIO
31 nsCreator installer.NamespaceCreator
32 createAccountAddr string
Giorgi Lekveishvili83b72192024-03-11 18:36:14 +040033 loginAddr string
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +040034}
35
Giorgi Lekveishvilic89b9002023-12-21 13:09:26 +040036func NewServer(
37 port int,
38 repo installer.RepoIO,
39 nsCreator installer.NamespaceCreator,
40 createAccountAddr string,
Giorgi Lekveishvili83b72192024-03-11 18:36:14 +040041 loginAddr string,
Giorgi Lekveishvilic89b9002023-12-21 13:09:26 +040042) *Server {
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +040043 return &Server{
44 port,
45 repo,
Giorgi Lekveishvili7fb28bf2023-06-24 19:51:16 +040046 nsCreator,
Giorgi Lekveishvilic89b9002023-12-21 13:09:26 +040047 createAccountAddr,
Giorgi Lekveishvili83b72192024-03-11 18:36:14 +040048 loginAddr,
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +040049 }
50}
51
52func (s *Server) Start() {
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +040053 r := mux.NewRouter()
54 r.PathPrefix("/static/").Handler(http.FileServer(http.FS(staticAssets)))
55 r.Path("/").Methods("POST").HandlerFunc(s.createAdminAccount)
56 r.Path("/").Methods("GET").HandlerFunc(s.createAdminAccountForm)
57 http.Handle("/", r)
58 log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", s.port), nil))
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +040059}
60
DTabidze52593392024-03-08 12:53:20 +040061func (s *Server) createAdminAccountForm(w http.ResponseWriter, r *http.Request) {
62 renderRegistrationForm(w, formData{})
63}
64
65type formData struct {
66 UsernameErrors []string
67 PasswordErrors []string
68 Data createAccountReq
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +040069}
70
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +040071type createAccountReq struct {
Giorgi Lekveishvili260a97d2023-12-08 15:04:16 +040072 Username string `json:"username,omitempty"`
Giorgi Lekveishvilic89b9002023-12-21 13:09:26 +040073 Password string `json:"password,omitempty"`
Giorgi Lekveishvili260a97d2023-12-08 15:04:16 +040074 SecretToken string `json:"secretToken,omitempty"`
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +040075}
76
Giorgi Lekveishvilic89b9002023-12-21 13:09:26 +040077type apiCreateAccountReq struct {
78 Username string `json:"username,omitempty"`
79 Password string `json:"password,omitempty"`
80}
81
DTabidze52593392024-03-08 12:53:20 +040082type ValidationError struct {
83 Field string `json:"field"`
84 Message string `json:"message"`
85}
86
87type ErrorResponse struct {
88 Errors []ValidationError `json:"errors"`
89}
90
Giorgi Lekveishvili6b887be2023-06-22 14:38:19 +040091func getFormValue(v url.Values, name string) (string, error) {
92 items, ok := v[name]
93 if !ok || len(items) != 1 {
94 return "", fmt.Errorf("%s not found", name)
95 }
96 return items[0], nil
97}
98
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +040099func extractReq(r *http.Request) (createAccountReq, error) {
100 var req createAccountReq
Giorgi Lekveishvili6b887be2023-06-22 14:38:19 +0400101 if err := func() error {
102 var err error
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +0400103 if err = r.ParseForm(); err != nil {
Giorgi Lekveishvili6b887be2023-06-22 14:38:19 +0400104 return err
105 }
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +0400106 if req.Username, err = getFormValue(r.PostForm, "username"); err != nil {
Giorgi Lekveishvili6b887be2023-06-22 14:38:19 +0400107 return err
108 }
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +0400109 if req.Password, err = getFormValue(r.PostForm, "password"); err != nil {
Giorgi Lekveishvili6b887be2023-06-22 14:38:19 +0400110 return err
111 }
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +0400112 if req.SecretToken, err = getFormValue(r.PostForm, "secret-token"); err != nil {
Giorgi Lekveishvili6b887be2023-06-22 14:38:19 +0400113 return err
114 }
115 return nil
116 }(); err != nil {
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +0400117 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
118 return createAccountReq{}, err
Giorgi Lekveishvili6b887be2023-06-22 14:38:19 +0400119 }
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400120 }
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +0400121 return req, nil
122}
123
DTabidze52593392024-03-08 12:53:20 +0400124func renderRegistrationForm(w http.ResponseWriter, data formData) {
125 tmpl, err := template.New("create-account").Parse(string(indexHtml))
126 if err != nil {
127 http.Error(w, err.Error(), http.StatusInternalServerError)
128 return
129 }
130 if err := tmpl.Execute(w, data); err != nil {
131 http.Error(w, err.Error(), http.StatusInternalServerError)
132 return
133 }
134}
135
Giorgi Lekveishvili83b72192024-03-11 18:36:14 +0400136func renderRegistrationSuccess(w http.ResponseWriter, loginAddr string) {
137 data := struct {
138 LoginAddr string
139 }{
140 LoginAddr: loginAddr,
141 }
142 tmpl, err := template.New("create-account-success").Parse(string(successHtml))
143 if err != nil {
144 http.Error(w, err.Error(), http.StatusInternalServerError)
145 return
146 }
147 if err := tmpl.Execute(w, data); err != nil {
148 http.Error(w, err.Error(), http.StatusInternalServerError)
149 return
150 }
151}
152
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +0400153func (s *Server) createAdminAccount(w http.ResponseWriter, r *http.Request) {
Giorgi Lekveishvili39913692023-12-05 08:58:08 +0400154 req, err := extractReq(r)
155 if err != nil {
156 http.Error(w, err.Error(), http.StatusInternalServerError)
157 return
158 }
Giorgi Lekveishvilic89b9002023-12-21 13:09:26 +0400159 {
160 var buf bytes.Buffer
161 cr := apiCreateAccountReq{req.Username, req.Password}
162 if err := json.NewEncoder(&buf).Encode(cr); err != nil {
163 http.Error(w, err.Error(), http.StatusInternalServerError)
164 return
165 }
166 resp, err := http.Post(s.createAccountAddr, "application/json", &buf)
167 if err != nil {
Giorgi Lekveishvili83399052024-02-14 13:27:30 +0400168 var respBody bytes.Buffer
169 if _, err := io.Copy(&respBody, resp.Body); err != nil {
170 http.Error(w, err.Error(), http.StatusInternalServerError)
171 }
172 respStr := respBody.String()
173 log.Println(respStr)
174 http.Error(w, respStr, http.StatusInternalServerError)
Giorgi Lekveishvilic89b9002023-12-21 13:09:26 +0400175 return
176 }
Giorgi Lekveishvilic89b9002023-12-21 13:09:26 +0400177 if resp.StatusCode != http.StatusOK {
DTabidze52593392024-03-08 12:53:20 +0400178 var errResponse ErrorResponse
179 if err := json.NewDecoder(resp.Body).Decode(&errResponse); err != nil {
180 http.Error(w, "Error Decoding JSON", http.StatusInternalServerError)
181 return
182 }
183 var usernameErrors, passwordErrors []string
184 for _, err := range errResponse.Errors {
185 if err.Field == "username" {
186 usernameErrors = append(usernameErrors, err.Message)
187 }
188 if err.Field == "password" {
189 passwordErrors = append(passwordErrors, err.Message)
190 }
191 }
192 renderRegistrationForm(w, formData{
193 usernameErrors,
194 passwordErrors,
195 req,
196 })
Giorgi Lekveishvilic89b9002023-12-21 13:09:26 +0400197 return
198 }
199 }
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400200 {
Giorgi Lekveishvili7fb28bf2023-06-24 19:51:16 +0400201 config, err := s.repo.ReadConfig()
202 if err != nil {
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +0400203 http.Error(w, err.Error(), http.StatusInternalServerError)
204 return
Giorgi Lekveishvili7fb28bf2023-06-24 19:51:16 +0400205 }
206 if err != nil {
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +0400207 http.Error(w, err.Error(), http.StatusInternalServerError)
208 return
Giorgi Lekveishvili7fb28bf2023-06-24 19:51:16 +0400209 }
Giorgi Lekveishvili6e813182023-06-30 13:45:30 +0400210 nsGen := installer.NewPrefixGenerator(config.Values.NamespacePrefix)
211 suffixGen := installer.NewEmptySuffixGenerator()
Giorgi Lekveishvili7fb28bf2023-06-24 19:51:16 +0400212 appManager, err := installer.NewAppManager(s.repo, s.nsCreator)
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400213 if err != nil {
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +0400214 http.Error(w, err.Error(), http.StatusInternalServerError)
215 return
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400216 }
217 appsRepo := installer.NewInMemoryAppRepository(installer.CreateAllApps())
218 {
Giorgi Lekveishvili39913692023-12-05 08:58:08 +0400219 app, err := appsRepo.Find("headscale-user")
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400220 if err != nil {
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +0400221 http.Error(w, err.Error(), http.StatusInternalServerError)
222 return
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400223 }
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400224 if err := appManager.Install(app, nsGen, suffixGen, map[string]any{
Giorgi Lekveishvilie009a5d2024-01-05 14:10:11 +0400225 "username": req.Username,
226 "preAuthKey": map[string]any{
227 "enabled": false,
Giorgi Lekveishvili39913692023-12-05 08:58:08 +0400228 },
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400229 }); err != nil {
Giorgi Lekveishvili123a3672023-12-04 13:01:29 +0400230 http.Error(w, err.Error(), http.StatusInternalServerError)
231 return
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400232 }
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400233 }
234 }
Giorgi Lekveishvili83b72192024-03-11 18:36:14 +0400235 renderRegistrationSuccess(w, s.loginAddr)
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400236}