feat: update user profile
This commit is contained in:
		
							parent
							
								
									24a0a9f5ee
								
							
						
					
					
						commit
						baca0b8488
					
				|  | @ -100,6 +100,63 @@ func Logout() echo.HandlerFunc { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // LoadProfile to load user information
 | ||||||
|  | func LoadProfile(db store.IStore) echo.HandlerFunc { | ||||||
|  | 	return func(c echo.Context) error { | ||||||
|  | 
 | ||||||
|  | 		userInfo, err := db.GetUser() | ||||||
|  | 		if err != nil { | ||||||
|  | 			log.Error("Cannot get user information: ", err) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		return c.Render(http.StatusOK, "profile.html", map[string]interface{}{ | ||||||
|  | 			"baseData": model.BaseData{Active: "profile", CurrentUser: currentUser(c)}, | ||||||
|  | 			"userInfo": userInfo, | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // UpdateProfile to update user information
 | ||||||
|  | func UpdateProfile(db store.IStore) echo.HandlerFunc { | ||||||
|  | 	return func(c echo.Context) error { | ||||||
|  | 		data := make(map[string]interface{}) | ||||||
|  | 		err := json.NewDecoder(c.Request().Body).Decode(&data) | ||||||
|  | 
 | ||||||
|  | 		if err != nil { | ||||||
|  | 			return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Bad post data"}) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		username := data["username"].(string) | ||||||
|  | 		password := data["password"].(string) | ||||||
|  | 
 | ||||||
|  | 		user, err := db.GetUser() | ||||||
|  | 		if err != nil { | ||||||
|  | 			return c.JSON(http.StatusNotFound, jsonHTTPResponse{false, err.Error()}) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if username == "" { | ||||||
|  | 			return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Please provide a valid username"}) | ||||||
|  | 		} else { | ||||||
|  | 			user.Username = username | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if password != "" { | ||||||
|  | 			hash, err := util.HashPassword(password) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, err.Error()}) | ||||||
|  | 			} | ||||||
|  | 			user.PasswordHash = hash | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if err := db.SaveUser(user); err != nil { | ||||||
|  | 			return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, err.Error()}) | ||||||
|  | 		} | ||||||
|  | 		log.Infof("Updated admin user information successfully") | ||||||
|  | 
 | ||||||
|  | 		return c.JSON(http.StatusOK, jsonHTTPResponse{true, "Updated admin user information successfully"}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // WireGuardClients handler
 | // WireGuardClients handler
 | ||||||
| func WireGuardClients(db store.IStore) echo.HandlerFunc { | func WireGuardClients(db store.IStore) echo.HandlerFunc { | ||||||
| 	return func(c echo.Context) error { | 	return func(c echo.Context) error { | ||||||
|  |  | ||||||
							
								
								
									
										4
									
								
								main.go
								
								
								
								
							
							
						
						
									
										4
									
								
								main.go
								
								
								
								
							|  | @ -135,6 +135,9 @@ func main() { | ||||||
| 	if !util.DisableLogin { | 	if !util.DisableLogin { | ||||||
| 		app.GET(util.BasePath+"/login", handler.LoginPage()) | 		app.GET(util.BasePath+"/login", handler.LoginPage()) | ||||||
| 		app.POST(util.BasePath+"/login", handler.Login(db)) | 		app.POST(util.BasePath+"/login", handler.Login(db)) | ||||||
|  | 		app.GET(util.BasePath+"/logout", handler.Logout(), handler.ValidSession) | ||||||
|  | 		app.GET(util.BasePath+"/profile", handler.LoadProfile(db), handler.ValidSession) | ||||||
|  | 		app.POST(util.BasePath+"/profile", handler.UpdateProfile(db), handler.ValidSession) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	var sendmail emailer.Emailer | 	var sendmail emailer.Emailer | ||||||
|  | @ -145,7 +148,6 @@ func main() { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	app.GET(util.BasePath+"/_health", handler.Health()) | 	app.GET(util.BasePath+"/_health", handler.Health()) | ||||||
| 	app.GET(util.BasePath+"/logout", handler.Logout(), handler.ValidSession) |  | ||||||
| 	app.POST(util.BasePath+"/new-client", handler.NewClient(db), handler.ValidSession, handler.ContentTypeJson) | 	app.POST(util.BasePath+"/new-client", handler.NewClient(db), handler.ValidSession, handler.ContentTypeJson) | ||||||
| 	app.POST(util.BasePath+"/update-client", handler.UpdateClient(db), handler.ValidSession, handler.ContentTypeJson) | 	app.POST(util.BasePath+"/update-client", handler.UpdateClient(db), handler.ValidSession, handler.ContentTypeJson) | ||||||
| 	app.POST(util.BasePath+"/email-client", handler.EmailClient(db, sendmail, defaultEmailSubject, defaultEmailContent), handler.ValidSession, handler.ContentTypeJson) | 	app.POST(util.BasePath+"/email-client", handler.EmailClient(db, sendmail, defaultEmailSubject, defaultEmailContent), handler.ValidSession, handler.ContentTypeJson) | ||||||
|  |  | ||||||
|  | @ -63,6 +63,11 @@ func New(tmplBox *rice.Box, extraData map[string]string, secret []byte) *echo.Ec | ||||||
| 		log.Fatal(err) | 		log.Fatal(err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	tmplProfileString, err := tmplBox.String("profile.html") | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Fatal(err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	tmplClientsString, err := tmplBox.String("clients.html") | 	tmplClientsString, err := tmplBox.String("clients.html") | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Fatal(err) | 		log.Fatal(err) | ||||||
|  | @ -94,6 +99,7 @@ func New(tmplBox *rice.Box, extraData map[string]string, secret []byte) *echo.Ec | ||||||
| 	} | 	} | ||||||
| 	templates := make(map[string]*template.Template) | 	templates := make(map[string]*template.Template) | ||||||
| 	templates["login.html"] = template.Must(template.New("login").Funcs(funcs).Parse(tmplLoginString)) | 	templates["login.html"] = template.Must(template.New("login").Funcs(funcs).Parse(tmplLoginString)) | ||||||
|  | 	templates["profile.html"] = template.Must(template.New("profile").Funcs(funcs).Parse(tmplBaseString + tmplProfileString)) | ||||||
| 	templates["clients.html"] = template.Must(template.New("clients").Funcs(funcs).Parse(tmplBaseString + tmplClientsString)) | 	templates["clients.html"] = template.Must(template.New("clients").Funcs(funcs).Parse(tmplBaseString + tmplClientsString)) | ||||||
| 	templates["server.html"] = template.Must(template.New("server").Funcs(funcs).Parse(tmplBaseString + tmplServerString)) | 	templates["server.html"] = template.Must(template.New("server").Funcs(funcs).Parse(tmplBaseString + tmplServerString)) | ||||||
| 	templates["global_settings.html"] = template.Must(template.New("global_settings").Funcs(funcs).Parse(tmplBaseString + tmplGlobalSettingsString)) | 	templates["global_settings.html"] = template.Must(template.New("global_settings").Funcs(funcs).Parse(tmplBaseString + tmplGlobalSettingsString)) | ||||||
|  |  | ||||||
|  | @ -127,6 +127,11 @@ func (o *JsonDB) GetUser() (model.User, error) { | ||||||
| 	return user, o.conn.Read("server", "users", &user) | 	return user, o.conn.Read("server", "users", &user) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // SaveUser func to user info to the database
 | ||||||
|  | func (o *JsonDB) SaveUser(user model.User) error { | ||||||
|  | 	return o.conn.Write("server", "users", user) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // GetGlobalSettings func to query global settings from the database
 | // GetGlobalSettings func to query global settings from the database
 | ||||||
| func (o *JsonDB) GetGlobalSettings() (model.GlobalSetting, error) { | func (o *JsonDB) GetGlobalSettings() (model.GlobalSetting, error) { | ||||||
| 	settings := model.GlobalSetting{} | 	settings := model.GlobalSetting{} | ||||||
|  |  | ||||||
|  | @ -7,6 +7,7 @@ import ( | ||||||
| type IStore interface { | type IStore interface { | ||||||
| 	Init() error | 	Init() error | ||||||
| 	GetUser() (model.User, error) | 	GetUser() (model.User, error) | ||||||
|  | 	SaveUser(user model.User) error | ||||||
| 	GetGlobalSettings() (model.GlobalSetting, error) | 	GetGlobalSettings() (model.GlobalSetting, error) | ||||||
| 	GetServer() (model.Server, error) | 	GetServer() (model.Server, error) | ||||||
| 	GetClients(hasQRCode bool) ([]model.ClientData, error) | 	GetClients(hasQRCode bool) ([]model.ClientData, error) | ||||||
|  |  | ||||||
|  | @ -87,7 +87,11 @@ | ||||||
|                         <i class="nav-icon fas fa-2x fa-user"></i> |                         <i class="nav-icon fas fa-2x fa-user"></i> | ||||||
|                     </div> |                     </div> | ||||||
|                     <div class="info"> |                     <div class="info"> | ||||||
|                         <a href="#" class="d-block">{{if .baseData.CurrentUser}} {{.baseData.CurrentUser}} {{else}} Administrator {{end}}</a> |                         {{if .baseData.CurrentUser}} | ||||||
|  |                         <a href="{{.basePath}}/profile" class="d-block">{{.baseData.CurrentUser}}</a> | ||||||
|  |                         {{else}} | ||||||
|  |                         <a href="#" class="d-block">Administrator</a> | ||||||
|  |                         {{end}} | ||||||
|                     </div> |                     </div> | ||||||
|                 </div> |                 </div> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -0,0 +1,110 @@ | ||||||
|  | {{ define "title"}} | ||||||
|  | Profile | ||||||
|  | {{ end }} | ||||||
|  | 
 | ||||||
|  | {{ define "top_css"}} | ||||||
|  | {{ end }} | ||||||
|  | 
 | ||||||
|  | {{ define "username"}} | ||||||
|  | {{ .username }} | ||||||
|  | {{ end }} | ||||||
|  | 
 | ||||||
|  | {{ define "page_title"}} | ||||||
|  | Profile | ||||||
|  | {{ end }} | ||||||
|  | 
 | ||||||
|  | {{ define "page_content"}} | ||||||
|  | <section class="content"> | ||||||
|  |     <div class="container-fluid"> | ||||||
|  |         <!-- <h5 class="mt-4 mb-2">Global Settings</h5> --> | ||||||
|  |         <div class="row"> | ||||||
|  |             <!-- left column --> | ||||||
|  |             <div class="col-md-6"> | ||||||
|  |                 <div class="card card-success"> | ||||||
|  |                     <div class="card-header"> | ||||||
|  |                         <h3 class="card-title">Update user information</h3> | ||||||
|  |                     </div> | ||||||
|  |                     <!-- /.card-header --> | ||||||
|  |                     <!-- form start --> | ||||||
|  |                     <form role="form" id="frm_profile" name="frm_profile"> | ||||||
|  |                         <div class="card-body"> | ||||||
|  |                             <div class="form-group"> | ||||||
|  |                                 <label for="username" class="control-label">Username</label> | ||||||
|  |                                 <input type="text" class="form-control" name="username" id="username" | ||||||
|  |                                        value="{{ .userInfo.Username }}"> | ||||||
|  |                             </div> | ||||||
|  |                             <div class="form-group"> | ||||||
|  |                                 <label for="password" class="control-label">Password</label> | ||||||
|  |                                 <input type="password" class="form-control" name="password" id="password" | ||||||
|  |                                        value="" placeholder="Leave empty to keep the password unchanged"> | ||||||
|  |                             </div> | ||||||
|  |                             <!-- /.card-body --> | ||||||
|  |                             <div class="card-footer"> | ||||||
|  |                                 <button type="submit" class="btn btn-success" id="update">Update</button> | ||||||
|  |                             </div> | ||||||
|  |                         </div> | ||||||
|  |                     </form> | ||||||
|  |                 </div> | ||||||
|  |                 <!-- /.card --> | ||||||
|  |             </div> | ||||||
|  |         </div> | ||||||
|  |         <!-- /.row --> | ||||||
|  |     </div> | ||||||
|  | </section> | ||||||
|  | {{ end }} | ||||||
|  | 
 | ||||||
|  | {{ define "bottom_js"}} | ||||||
|  | <script> | ||||||
|  |   function updateUserInfo() { | ||||||
|  |     const username = $("#username").val(); | ||||||
|  |     const password = $("#password").val(); | ||||||
|  |     const data = {"username": username, "password": password}; | ||||||
|  |     $.ajax({ | ||||||
|  |       cache: false, | ||||||
|  |       method: 'POST', | ||||||
|  |       url: '{{.basePath}}/profile', | ||||||
|  |       dataType: 'json', | ||||||
|  |       contentType: "application/json", | ||||||
|  |       data: JSON.stringify(data), | ||||||
|  |       success: function (data) { | ||||||
|  |         toastr.success("Updated admin user information successfully"); | ||||||
|  |       }, | ||||||
|  |       error: function (jqXHR, exception) { | ||||||
|  |         const responseJson = jQuery.parseJSON(jqXHR.responseText); | ||||||
|  |         toastr.error(responseJson['message']); | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   $(document).ready(function () { | ||||||
|  |     $.validator.setDefaults({ | ||||||
|  |       submitHandler: function () { | ||||||
|  |         updateUserInfo(); | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  |     $("#frm_profile").validate({ | ||||||
|  |       rules: { | ||||||
|  |         username: { | ||||||
|  |           required: true | ||||||
|  |         } | ||||||
|  |       }, | ||||||
|  |       messages: { | ||||||
|  |         username: { | ||||||
|  |           required: "Please enter a username", | ||||||
|  |         } | ||||||
|  |       }, | ||||||
|  |       errorElement: 'span', | ||||||
|  |       errorPlacement: function (error, element) { | ||||||
|  |         error.addClass('invalid-feedback'); | ||||||
|  |         element.closest('.form-group').append(error); | ||||||
|  |       }, | ||||||
|  |       highlight: function (element, errorClass, validClass) { | ||||||
|  |         $(element).addClass('is-invalid'); | ||||||
|  |       }, | ||||||
|  |       unhighlight: function (element, errorClass, validClass) { | ||||||
|  |         $(element).removeClass('is-invalid'); | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | </script> | ||||||
|  | {{ end }} | ||||||
|  | @ -110,7 +110,7 @@ Wireguard Server Settings | ||||||
|             </div> |             </div> | ||||||
|             <div class="modal-body"> |             <div class="modal-body"> | ||||||
|                 <p>Are you sure to generate a new key pair for the Wireguard server?<br/> |                 <p>Are you sure to generate a new key pair for the Wireguard server?<br/> | ||||||
|                 The existing Clients's peer public key need to be updated to keep the connection working.</p> |                 The existing Client's peer public key need to be updated to keep the connection working.</p> | ||||||
|             </div> |             </div> | ||||||
|             <div class="modal-footer justify-content-between"> |             <div class="modal-footer justify-content-between"> | ||||||
|                 <button type="button" class="btn btn-outline-dark" data-dismiss="modal">Cancel</button> |                 <button type="button" class="btn btn-outline-dark" data-dismiss="modal">Cancel</button> | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue