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
 | ||||
| func WireGuardClients(db store.IStore) echo.HandlerFunc { | ||||
| 	return func(c echo.Context) error { | ||||
|  |  | |||
							
								
								
									
										4
									
								
								main.go
								
								
								
								
							
							
						
						
									
										4
									
								
								main.go
								
								
								
								
							|  | @ -135,6 +135,9 @@ func main() { | |||
| 	if !util.DisableLogin { | ||||
| 		app.GET(util.BasePath+"/login", handler.LoginPage()) | ||||
| 		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 | ||||
|  | @ -145,7 +148,6 @@ func main() { | |||
| 	} | ||||
| 
 | ||||
| 	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+"/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) | ||||
|  |  | |||
|  | @ -63,6 +63,11 @@ func New(tmplBox *rice.Box, extraData map[string]string, secret []byte) *echo.Ec | |||
| 		log.Fatal(err) | ||||
| 	} | ||||
| 
 | ||||
| 	tmplProfileString, err := tmplBox.String("profile.html") | ||||
| 	if err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| 
 | ||||
| 	tmplClientsString, err := tmplBox.String("clients.html") | ||||
| 	if err != nil { | ||||
| 		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["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["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)) | ||||
|  |  | |||
|  | @ -127,6 +127,11 @@ func (o *JsonDB) GetUser() (model.User, error) { | |||
| 	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
 | ||||
| func (o *JsonDB) GetGlobalSettings() (model.GlobalSetting, error) { | ||||
| 	settings := model.GlobalSetting{} | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ import ( | |||
| type IStore interface { | ||||
| 	Init() error | ||||
| 	GetUser() (model.User, error) | ||||
| 	SaveUser(user model.User) error | ||||
| 	GetGlobalSettings() (model.GlobalSetting, error) | ||||
| 	GetServer() (model.Server, error) | ||||
| 	GetClients(hasQRCode bool) ([]model.ClientData, error) | ||||
|  |  | |||
|  | @ -87,7 +87,11 @@ | |||
|                         <i class="nav-icon fas fa-2x fa-user"></i> | ||||
|                     </div> | ||||
|                     <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> | ||||
| 
 | ||||
|  |  | |||
|  | @ -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 class="modal-body"> | ||||
|                 <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 class="modal-footer justify-content-between"> | ||||
|                 <button type="button" class="btn btn-outline-dark" data-dismiss="modal">Cancel</button> | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue