feat: allow password change
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				ci/woodpecker/tag/release Pipeline was successful
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	ci/woodpecker/tag/release Pipeline was successful
				
			This commit is contained in:
		
							parent
							
								
									ece8280358
								
							
						
					
					
						commit
						6aedfc794f
					
				
					 4 changed files with 203 additions and 22 deletions
				
			
		| 
						 | 
					@ -2,6 +2,8 @@ package admin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"embed"
 | 
						"embed"
 | 
				
			||||||
 | 
						"encoding/gob"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
	"html/template"
 | 
						"html/template"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
| 
						 | 
					@ -28,6 +30,11 @@ type FlashMessage struct {
 | 
				
			||||||
	Message  string
 | 
						Message  string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						// Register the FlashMessage type with gob package for session serialization
 | 
				
			||||||
 | 
						gob.Register(FlashMessage{})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TemplateData holds data for rendering templates
 | 
					// TemplateData holds data for rendering templates
 | 
				
			||||||
type TemplateData struct {
 | 
					type TemplateData struct {
 | 
				
			||||||
	User          *model.User
 | 
						User          *model.User
 | 
				
			||||||
| 
						 | 
					@ -52,8 +59,13 @@ type Admin struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// New creates a new Admin instance
 | 
					// New creates a new Admin instance
 | 
				
			||||||
func New(cfg *config.Config, database *db.Database) *Admin {
 | 
					func New(cfg *config.Config, database *db.Database) *Admin {
 | 
				
			||||||
	// Create session store
 | 
						// Create session store with appropriate options
 | 
				
			||||||
	store := sessions.NewCookieStore([]byte(cfg.SecretKey))
 | 
						store := sessions.NewCookieStore([]byte(cfg.SecretKey))
 | 
				
			||||||
 | 
						store.Options = &sessions.Options{
 | 
				
			||||||
 | 
							Path:     "/admin",
 | 
				
			||||||
 | 
							MaxAge:   3600 * 24 * 7, // 1 week
 | 
				
			||||||
 | 
							HttpOnly: true,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Load templates
 | 
						// Load templates
 | 
				
			||||||
	templates := make(map[string]*template.Template)
 | 
						templates := make(map[string]*template.Template)
 | 
				
			||||||
| 
						 | 
					@ -79,6 +91,7 @@ func New(cfg *config.Config, database *db.Database) *Admin {
 | 
				
			||||||
	templateFiles := []string{
 | 
						templateFiles := []string{
 | 
				
			||||||
		"index.html",
 | 
							"index.html",
 | 
				
			||||||
		"login.html",
 | 
							"login.html",
 | 
				
			||||||
 | 
							"change_password.html",
 | 
				
			||||||
		"channel_list.html",
 | 
							"channel_list.html",
 | 
				
			||||||
		"channel_detail.html",
 | 
							"channel_detail.html",
 | 
				
			||||||
		"plugin_list.html",
 | 
							"plugin_list.html",
 | 
				
			||||||
| 
						 | 
					@ -122,6 +135,7 @@ func (a *Admin) RegisterRoutes(mux *http.ServeMux) {
 | 
				
			||||||
	mux.HandleFunc("/admin/", a.handleIndex)
 | 
						mux.HandleFunc("/admin/", a.handleIndex)
 | 
				
			||||||
	mux.HandleFunc("/admin/login", a.handleLogin)
 | 
						mux.HandleFunc("/admin/login", a.handleLogin)
 | 
				
			||||||
	mux.HandleFunc("/admin/logout", a.handleLogout)
 | 
						mux.HandleFunc("/admin/logout", a.handleLogout)
 | 
				
			||||||
 | 
						mux.HandleFunc("/admin/change-password", a.handleChangePassword)
 | 
				
			||||||
	mux.HandleFunc("/admin/plugins", a.handlePluginList)
 | 
						mux.HandleFunc("/admin/plugins", a.handlePluginList)
 | 
				
			||||||
	mux.HandleFunc("/admin/channels", a.handleChannelList)
 | 
						mux.HandleFunc("/admin/channels", a.handleChannelList)
 | 
				
			||||||
	mux.HandleFunc("/admin/channels/", a.handleChannelDetail)
 | 
						mux.HandleFunc("/admin/channels/", a.handleChannelDetail)
 | 
				
			||||||
| 
						 | 
					@ -131,7 +145,11 @@ func (a *Admin) RegisterRoutes(mux *http.ServeMux) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// getCurrentUser gets the current user from the session
 | 
					// getCurrentUser gets the current user from the session
 | 
				
			||||||
func (a *Admin) getCurrentUser(r *http.Request) *model.User {
 | 
					func (a *Admin) getCurrentUser(r *http.Request) *model.User {
 | 
				
			||||||
	session, _ := a.store.Get(r, sessionKey)
 | 
						session, err := a.store.Get(r, sessionKey)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							fmt.Printf("Error getting session for user retrieval: %v\n", err)
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Check if user is logged in
 | 
						// Check if user is logged in
 | 
				
			||||||
	userID, ok := session.Values["user_id"].(int64)
 | 
						userID, ok := session.Values["user_id"].(int64)
 | 
				
			||||||
| 
						 | 
					@ -142,6 +160,7 @@ func (a *Admin) getCurrentUser(r *http.Request) *model.User {
 | 
				
			||||||
	// Get user from database
 | 
						// Get user from database
 | 
				
			||||||
	user, err := a.db.GetUserByID(userID)
 | 
						user, err := a.db.GetUserByID(userID)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
 | 
							fmt.Printf("Error retrieving user from database: %v\n", err)
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -150,32 +169,63 @@ func (a *Admin) getCurrentUser(r *http.Request) *model.User {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// isLoggedIn checks if the user is logged in
 | 
					// isLoggedIn checks if the user is logged in
 | 
				
			||||||
func (a *Admin) isLoggedIn(r *http.Request) bool {
 | 
					func (a *Admin) isLoggedIn(r *http.Request) bool {
 | 
				
			||||||
	session, _ := a.store.Get(r, sessionKey)
 | 
						session, err := a.store.Get(r, sessionKey)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							fmt.Printf("Error getting session for login check: %v\n", err)
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	return session.Values["logged_in"] == true
 | 
						return session.Values["logged_in"] == true
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// addFlash adds a flash message to the session
 | 
					// addFlash adds a flash message to the session
 | 
				
			||||||
func (a *Admin) addFlash(w http.ResponseWriter, r *http.Request, message string, category string) {
 | 
					func (a *Admin) addFlash(w http.ResponseWriter, r *http.Request, message string, category string) {
 | 
				
			||||||
	session, _ := a.store.Get(r, sessionKey)
 | 
						session, err := a.store.Get(r, sessionKey)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							// If there's an error getting the session, create a new one
 | 
				
			||||||
 | 
							session = sessions.NewSession(a.store, sessionKey)
 | 
				
			||||||
 | 
							session.Options = &sessions.Options{
 | 
				
			||||||
 | 
								Path:     "/admin",
 | 
				
			||||||
 | 
								MaxAge:   3600 * 24 * 7, // 1 week
 | 
				
			||||||
 | 
								HttpOnly: true,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Add flash message
 | 
						// Map internal categories to Bootstrap alert classes
 | 
				
			||||||
	flashes := session.Flashes()
 | 
						alertClass := category
 | 
				
			||||||
	if flashes == nil {
 | 
						switch category {
 | 
				
			||||||
		flashes = make([]interface{}, 0)
 | 
						case "success":
 | 
				
			||||||
 | 
							alertClass = "success"
 | 
				
			||||||
 | 
						case "danger":
 | 
				
			||||||
 | 
							alertClass = "danger"
 | 
				
			||||||
 | 
						case "warning":
 | 
				
			||||||
 | 
							alertClass = "warning"
 | 
				
			||||||
 | 
						case "info":
 | 
				
			||||||
 | 
							alertClass = "info"
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							alertClass = "info"
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	flash := FlashMessage{
 | 
						flash := FlashMessage{
 | 
				
			||||||
		Category: category,
 | 
							Category: alertClass,
 | 
				
			||||||
		Message:  message,
 | 
							Message:  message,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	session.AddFlash(flash)
 | 
						session.AddFlash(flash)
 | 
				
			||||||
	session.Save(r, w)
 | 
						err = session.Save(r, w)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							// Log the error or handle it appropriately
 | 
				
			||||||
 | 
							fmt.Printf("Error saving session: %v\n", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// getFlashes gets all flash messages from the session
 | 
					// getFlashes gets all flash messages from the session
 | 
				
			||||||
func (a *Admin) getFlashes(w http.ResponseWriter, r *http.Request) []FlashMessage {
 | 
					func (a *Admin) getFlashes(w http.ResponseWriter, r *http.Request) []FlashMessage {
 | 
				
			||||||
	session, _ := a.store.Get(r, sessionKey)
 | 
						session, err := a.store.Get(r, sessionKey)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							// If there's an error getting the session, return an empty slice
 | 
				
			||||||
 | 
							fmt.Printf("Error getting session for flashes: %v\n", err)
 | 
				
			||||||
 | 
							return []FlashMessage{}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Get flash messages
 | 
						// Get flash messages
 | 
				
			||||||
	flashes := session.Flashes()
 | 
						flashes := session.Flashes()
 | 
				
			||||||
| 
						 | 
					@ -188,7 +238,10 @@ func (a *Admin) getFlashes(w http.ResponseWriter, r *http.Request) []FlashMessag
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Save session to clear flashes
 | 
						// Save session to clear flashes
 | 
				
			||||||
	session.Save(r, w)
 | 
						err = session.Save(r, w)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							fmt.Printf("Error saving session after getting flashes: %v\n", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return messages
 | 
						return messages
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -299,10 +352,19 @@ func (a *Admin) handleLogin(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
// handleLogout handles the logout route
 | 
					// handleLogout handles the logout route
 | 
				
			||||||
func (a *Admin) handleLogout(w http.ResponseWriter, r *http.Request) {
 | 
					func (a *Admin) handleLogout(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
	// Clear session
 | 
						// Clear session
 | 
				
			||||||
	session, _ := a.store.Get(r, sessionKey)
 | 
						session, err := a.store.Get(r, sessionKey)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							fmt.Printf("Error getting session for logout: %v\n", err)
 | 
				
			||||||
 | 
							http.Redirect(w, r, "/admin/login", http.StatusSeeOther)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
	session.Values = make(map[interface{}]interface{})
 | 
						session.Values = make(map[interface{}]interface{})
 | 
				
			||||||
	session.Options.MaxAge = -1 // Delete session
 | 
						session.Options.MaxAge = -1 // Delete session
 | 
				
			||||||
	session.Save(r, w)
 | 
						err = session.Save(r, w)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							fmt.Printf("Error saving session for logout: %v\n", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	a.addFlash(w, r, "You were logged out", "success")
 | 
						a.addFlash(w, r, "You were logged out", "success")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -310,6 +372,74 @@ func (a *Admin) handleLogout(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
	http.Redirect(w, r, "/admin/login", http.StatusSeeOther)
 | 
						http.Redirect(w, r, "/admin/login", http.StatusSeeOther)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// handleChangePassword handles the change password route
 | 
				
			||||||
 | 
					func (a *Admin) handleChangePassword(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
 | 
						// Check if user is logged in
 | 
				
			||||||
 | 
						if !a.isLoggedIn(r) {
 | 
				
			||||||
 | 
							http.Redirect(w, r, "/admin/login", http.StatusSeeOther)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Get current user
 | 
				
			||||||
 | 
						user := a.getCurrentUser(r)
 | 
				
			||||||
 | 
						if user == nil {
 | 
				
			||||||
 | 
							http.Redirect(w, r, "/admin/login", http.StatusSeeOther)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Handle form submission
 | 
				
			||||||
 | 
						if r.Method == http.MethodPost {
 | 
				
			||||||
 | 
							// Parse form
 | 
				
			||||||
 | 
							if err := r.ParseForm(); err != nil {
 | 
				
			||||||
 | 
								http.Error(w, "Bad request", http.StatusBadRequest)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Get form values
 | 
				
			||||||
 | 
							currentPassword := r.FormValue("current_password")
 | 
				
			||||||
 | 
							newPassword := r.FormValue("new_password")
 | 
				
			||||||
 | 
							confirmPassword := r.FormValue("confirm_password")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Validate current password
 | 
				
			||||||
 | 
							_, err := a.db.CheckCredentials(user.Username, currentPassword)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								a.addFlash(w, r, "Current password is incorrect", "danger")
 | 
				
			||||||
 | 
								http.Redirect(w, r, "/admin/change-password", http.StatusSeeOther)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Validate new password and confirmation
 | 
				
			||||||
 | 
							if newPassword == "" {
 | 
				
			||||||
 | 
								a.addFlash(w, r, "New password cannot be empty", "danger")
 | 
				
			||||||
 | 
								http.Redirect(w, r, "/admin/change-password", http.StatusSeeOther)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if newPassword != confirmPassword {
 | 
				
			||||||
 | 
								a.addFlash(w, r, "New passwords do not match", "danger")
 | 
				
			||||||
 | 
								http.Redirect(w, r, "/admin/change-password", http.StatusSeeOther)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Update password
 | 
				
			||||||
 | 
							if err := a.db.UpdateUserPassword(user.ID, newPassword); err != nil {
 | 
				
			||||||
 | 
								a.addFlash(w, r, "Failed to update password: "+err.Error(), "danger")
 | 
				
			||||||
 | 
								http.Redirect(w, r, "/admin/change-password", http.StatusSeeOther)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Success
 | 
				
			||||||
 | 
							a.addFlash(w, r, "Password changed successfully", "success")
 | 
				
			||||||
 | 
							http.Redirect(w, r, "/admin/", http.StatusSeeOther)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Render change password template
 | 
				
			||||||
 | 
						a.render(w, r, "change_password.html", TemplateData{
 | 
				
			||||||
 | 
							Title: "Change Password",
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// handlePluginList handles the plugin list route
 | 
					// handlePluginList handles the plugin list route
 | 
				
			||||||
func (a *Admin) handlePluginList(w http.ResponseWriter, r *http.Request) {
 | 
					func (a *Admin) handlePluginList(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
	// Check if user is logged in
 | 
						// Check if user is logged in
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -28,8 +28,10 @@
 | 
				
			||||||
                            <a href="/admin/login">Log in</a>
 | 
					                            <a href="/admin/login">Log in</a>
 | 
				
			||||||
                            {{else}}
 | 
					                            {{else}}
 | 
				
			||||||
                            <div class="d-none d-xl-block pl-2">
 | 
					                            <div class="d-none d-xl-block pl-2">
 | 
				
			||||||
                                <div>{{.User.Username}} - <a class="mt-1 small"
 | 
					                                <div>{{.User.Username}} - 
 | 
				
			||||||
                                        href="/admin/logout">Log out</a></div>
 | 
					                                    <a class="mt-1 small" href="/admin/change-password">Change Password</a> | 
 | 
				
			||||||
 | 
					                                    <a class="mt-1 small" href="/admin/logout">Log out</a>
 | 
				
			||||||
 | 
					                                </div>
 | 
				
			||||||
                            </div>
 | 
					                            </div>
 | 
				
			||||||
                            </a>
 | 
					                            </a>
 | 
				
			||||||
                            {{end}}
 | 
					                            {{end}}
 | 
				
			||||||
| 
						 | 
					@ -100,14 +102,14 @@
 | 
				
			||||||
            {{end}}
 | 
					            {{end}}
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        {{range .Flash}}
 | 
					        <div class="container-xl mt-3">
 | 
				
			||||||
        <div class="card">
 | 
					            {{range .Flash}}
 | 
				
			||||||
            <div class="card-status-top bg-{{.Category}}"></div>
 | 
					            <div class="alert alert-{{.Category}} alert-dismissible" role="alert">
 | 
				
			||||||
            <div class="card-body">
 | 
					                {{.Message}}
 | 
				
			||||||
                <p>{{.Message}}</p>
 | 
					                <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
 | 
					            {{end}}
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        {{end}}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <div class="content">
 | 
					        <div class="content">
 | 
				
			||||||
            <div class="container-xl">
 | 
					            <div class="container-xl">
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										30
									
								
								internal/admin/templates/change_password.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								internal/admin/templates/change_password.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,30 @@
 | 
				
			||||||
 | 
					{{define "content"}}
 | 
				
			||||||
 | 
					<div class="row justify-content-center">
 | 
				
			||||||
 | 
					    <div class="col-md-6">
 | 
				
			||||||
 | 
					        <div class="card">
 | 
				
			||||||
 | 
					            <div class="card-header">
 | 
				
			||||||
 | 
					                <h3 class="card-title">Change Password</h3>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="card-body">
 | 
				
			||||||
 | 
					                <form method="post" action="/admin/change-password">
 | 
				
			||||||
 | 
					                    <div class="mb-3">
 | 
				
			||||||
 | 
					                        <label class="form-label">Current Password</label>
 | 
				
			||||||
 | 
					                        <input type="password" name="current_password" class="form-control" placeholder="Current Password" required>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                    <div class="mb-3">
 | 
				
			||||||
 | 
					                        <label class="form-label">New Password</label>
 | 
				
			||||||
 | 
					                        <input type="password" name="new_password" class="form-control" placeholder="New Password" required>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                    <div class="mb-3">
 | 
				
			||||||
 | 
					                        <label class="form-label">Confirm New Password</label>
 | 
				
			||||||
 | 
					                        <input type="password" name="confirm_password" class="form-control" placeholder="Confirm New Password" required>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                    <div class="form-footer">
 | 
				
			||||||
 | 
					                        <button type="submit" class="btn btn-primary">Change Password</button>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                </form>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					{{end}}
 | 
				
			||||||
| 
						 | 
					@ -572,6 +572,25 @@ func (d *Database) CheckCredentials(username, password string) (*model.User, err
 | 
				
			||||||
	}, nil
 | 
						}, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// UpdateUserPassword updates a user's password
 | 
				
			||||||
 | 
					func (d *Database) UpdateUserPassword(userID int64, newPassword string) error {
 | 
				
			||||||
 | 
						// Hash the new password
 | 
				
			||||||
 | 
						hashedPassword, err := hashPassword(newPassword)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Update the user's password
 | 
				
			||||||
 | 
						query := `
 | 
				
			||||||
 | 
							UPDATE users
 | 
				
			||||||
 | 
							SET password = ?
 | 
				
			||||||
 | 
							WHERE id = ?
 | 
				
			||||||
 | 
						`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, err = d.db.Exec(query, hashedPassword, userID)
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Helper function to hash password
 | 
					// Helper function to hash password
 | 
				
			||||||
func hashPassword(password string) (string, error) {
 | 
					func hashPassword(password string) (string, error) {
 | 
				
			||||||
	// Use bcrypt for secure password hashing
 | 
						// Use bcrypt for secure password hashing
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue