authentication with username & password
This commit is contained in:
parent
0e080dd391
commit
3d9c710985
4 changed files with 202 additions and 12 deletions
9
go.mod
9
go.mod
|
|
@ -1,8 +1,13 @@
|
||||||
module tms
|
module tms
|
||||||
|
|
||||||
go 1.22.0
|
go 1.23
|
||||||
|
|
||||||
|
toolchain go1.23.6
|
||||||
|
|
||||||
|
require golang.org/x/crypto v0.33.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||||
|
github.com/gorilla/sessions v1.4.0 // indirect
|
||||||
github.com/mattn/go-sqlite3 v1.14.24 // indirect
|
github.com/mattn/go-sqlite3 v1.14.24 // indirect
|
||||||
golang.org/x/crypto v0.33.0
|
|
||||||
)
|
)
|
||||||
|
|
|
||||||
4
go.sum
4
go.sum
|
|
@ -1,3 +1,7 @@
|
||||||
|
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
|
||||||
|
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
|
||||||
|
github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ=
|
||||||
|
github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik=
|
||||||
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
|
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
|
||||||
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||||
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
||||||
|
|
|
||||||
183
main.go
183
main.go
|
|
@ -5,9 +5,11 @@ import (
|
||||||
"embed"
|
"embed"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/gorilla/sessions"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
|
||||||
_ "github.com/mattn/go-sqlite3"
|
_ "github.com/mattn/go-sqlite3"
|
||||||
|
|
@ -19,6 +21,9 @@ var templatesFS embed.FS
|
||||||
//go:embed schema.sql
|
//go:embed schema.sql
|
||||||
var schemaSQL string
|
var schemaSQL string
|
||||||
|
|
||||||
|
// Store for sessions (adjust the key to something secure)
|
||||||
|
var store = sessions.NewCookieStore([]byte("your-secret-key"))
|
||||||
|
|
||||||
type Trainer struct {
|
type Trainer struct {
|
||||||
ID int
|
ID int
|
||||||
Name string
|
Name string
|
||||||
|
|
@ -64,13 +69,15 @@ func initDB() {
|
||||||
var err error
|
var err error
|
||||||
db, err = sql.Open("sqlite3", "training.db")
|
db, err = sql.Open("sqlite3", "training.db")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
log.Fatalf("Error opening database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = db.Exec(schemaSQL)
|
_, err = db.Exec(schemaSQL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
log.Fatalf("Error initializing database schema: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Println("Database initialized successfully")
|
||||||
}
|
}
|
||||||
|
|
||||||
var tmpl *template.Template
|
var tmpl *template.Template
|
||||||
|
|
@ -79,26 +86,33 @@ func initTemplates() {
|
||||||
var err error
|
var err error
|
||||||
tmpl, err = template.ParseFS(templatesFS, "templates/*.html")
|
tmpl, err = template.ParseFS(templatesFS, "templates/*.html")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Sprintf("Error parsing templates: %v", err))
|
log.Fatalf("Error parsing templates: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Println("Templates loaded successfully")
|
||||||
}
|
}
|
||||||
|
|
||||||
func homeHandler(w http.ResponseWriter, r *http.Request) {
|
func homeHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
log.Println("Handling home page request")
|
||||||
err := tmpl.ExecuteTemplate(w, "home.html", nil)
|
err := tmpl.ExecuteTemplate(w, "home.html", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Printf("Error in templating page home.html: %v", err)
|
||||||
http.Error(w, fmt.Sprintf("Error in templating page %v", err), http.StatusInternalServerError)
|
http.Error(w, fmt.Sprintf("Error in templating page %v", err), http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func trainingAreaListHandler(w http.ResponseWriter, r *http.Request) {
|
func trainingAreaListHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
log.Println("Fetching training areas and trainers")
|
||||||
areas, err := fetchTrainingAreasWithTrainers()
|
areas, err := fetchTrainingAreasWithTrainers()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Printf("Error fetching training areas: %v", err)
|
||||||
http.Error(w, "Database error", http.StatusInternalServerError)
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
trainers, err := fetchTrainers()
|
trainers, err := fetchTrainers()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Printf("Error fetching trainers: %v", err)
|
||||||
http.Error(w, "Database error", http.StatusInternalServerError)
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -113,12 +127,16 @@ func trainingAreaListHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
err = tmpl.ExecuteTemplate(w, "training_area_list.html", data)
|
err = tmpl.ExecuteTemplate(w, "training_area_list.html", data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Printf("Error in templating page training_area_list.html: %v", err)
|
||||||
http.Error(w, fmt.Sprintf("Error in templating page %v", err), http.StatusInternalServerError)
|
http.Error(w, fmt.Sprintf("Error in templating page %v", err), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Println("Training areas and trainers displayed successfully")
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchTrainingAreasWithTrainers() ([]TrainingArea, error) {
|
func fetchTrainingAreasWithTrainers() ([]TrainingArea, error) {
|
||||||
|
log.Println("Executing query to fetch training areas with trainers")
|
||||||
rows, err := db.Query(`
|
rows, err := db.Query(`
|
||||||
SELECT ta.id, ta.name, t.id, t.name, t.email
|
SELECT ta.id, ta.name, t.id, t.name, t.email
|
||||||
FROM training_areas ta
|
FROM training_areas ta
|
||||||
|
|
@ -126,6 +144,7 @@ func fetchTrainingAreasWithTrainers() ([]TrainingArea, error) {
|
||||||
LEFT JOIN trainers t ON tta.trainer_id = t.id
|
LEFT JOIN trainers t ON tta.trainer_id = t.id
|
||||||
ORDER BY ta.id, t.name`)
|
ORDER BY ta.id, t.name`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Printf("Database error while fetching training areas: %v", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
@ -141,6 +160,7 @@ func fetchTrainingAreasWithTrainers() ([]TrainingArea, error) {
|
||||||
|
|
||||||
err := rows.Scan(&areaID, &areaName, &trainerID, &trainerName, &trainerEmail)
|
err := rows.Scan(&areaID, &areaName, &trainerID, &trainerName, &trainerEmail)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Printf("Error scanning row for training area: %v", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -164,55 +184,67 @@ func fetchTrainingAreasWithTrainers() ([]TrainingArea, error) {
|
||||||
areas = append(areas, *area)
|
areas = append(areas, *area)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Printf("Found %d training areas", len(areas))
|
||||||
return areas, nil
|
return areas, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func assignTrainerToAreaHandler(w http.ResponseWriter, r *http.Request) {
|
func assignTrainerToAreaHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
log.Println("Handling trainer assignment to training area")
|
||||||
if r.Method == http.MethodPost {
|
if r.Method == http.MethodPost {
|
||||||
trainerID := r.FormValue("trainer_id")
|
trainerID := r.FormValue("trainer_id")
|
||||||
areaID := r.FormValue("training_area_id")
|
areaID := r.FormValue("training_area_id")
|
||||||
|
|
||||||
if trainerID == "" || areaID == "" {
|
if trainerID == "" || areaID == "" {
|
||||||
|
log.Println("Missing trainer or training area ID")
|
||||||
http.Error(w, "Trainer and Training Area are required", http.StatusBadRequest)
|
http.Error(w, "Trainer and Training Area are required", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := db.Exec("INSERT OR IGNORE INTO trainer_training_areas (trainer_id, training_area_id) VALUES (?, ?)", trainerID, areaID)
|
_, err := db.Exec("INSERT OR IGNORE INTO trainer_training_areas (trainer_id, training_area_id) VALUES (?, ?)", trainerID, areaID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Printf("Error assigning trainer to area: %v", err)
|
||||||
http.Error(w, "Database error", http.StatusInternalServerError)
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Println("Trainer assigned to area successfully")
|
||||||
http.Redirect(w, r, "/training-areas", http.StatusSeeOther)
|
http.Redirect(w, r, "/training-areas", http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func addTrainingAreaHandler(w http.ResponseWriter, r *http.Request) {
|
func addTrainingAreaHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
log.Println("Handling request to add a new training area")
|
||||||
if r.Method == http.MethodPost {
|
if r.Method == http.MethodPost {
|
||||||
name := r.FormValue("name")
|
name := r.FormValue("name")
|
||||||
if name == "" {
|
if name == "" {
|
||||||
|
log.Println("Training area name is required")
|
||||||
http.Error(w, "Name is required", http.StatusBadRequest)
|
http.Error(w, "Name is required", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := db.Exec("INSERT INTO training_areas (name) VALUES (?)", name)
|
_, err := db.Exec("INSERT INTO training_areas (name) VALUES (?)", name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Printf("Error adding training area: %v", err)
|
||||||
http.Error(w, "Database error", http.StatusInternalServerError)
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
log.Println("New training area added successfully")
|
||||||
http.Redirect(w, r, "/training-areas", http.StatusSeeOther)
|
http.Redirect(w, r, "/training-areas", http.StatusSeeOther)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err := tmpl.ExecuteTemplate(w, "add_training_area.html", nil)
|
err := tmpl.ExecuteTemplate(w, "add_training_area.html", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Printf("Error in templating page add_training_area.html: %v", err)
|
||||||
http.Error(w, fmt.Sprintf("Error in templating page %v", err), http.StatusInternalServerError)
|
http.Error(w, fmt.Sprintf("Error in templating page %v", err), http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchTrainings() ([]Training, error) {
|
func fetchTrainings() ([]Training, error) {
|
||||||
|
log.Println("Fetching trainings from the database")
|
||||||
rows, err := db.Query("SELECT id, title, training_type, mode, start, end, status FROM trainings")
|
rows, err := db.Query("SELECT id, title, training_type, mode, start, end, status FROM trainings")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Printf("Error fetching trainings: %v", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
@ -223,31 +255,39 @@ func fetchTrainings() ([]Training, error) {
|
||||||
var start, end string
|
var start, end string
|
||||||
err := rows.Scan(&t.ID, &t.Title, &t.TrainingType, &t.Mode, &start, &end, &t.Status)
|
err := rows.Scan(&t.ID, &t.Title, &t.TrainingType, &t.Mode, &start, &end, &t.Status)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Printf("Error scanning row for training: %v", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
t.Start, _ = time.Parse("2006-01-02 15:04:05", start)
|
t.Start, _ = time.Parse("2006-01-02 15:04:05", start)
|
||||||
t.End, _ = time.Parse("2006-01-02 15:04:05", end)
|
t.End, _ = time.Parse("2006-01-02 15:04:05", end)
|
||||||
trainings = append(trainings, t)
|
trainings = append(trainings, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Printf("Found %d trainings", len(trainings))
|
||||||
return trainings, nil
|
return trainings, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func trainingListHandler(w http.ResponseWriter, r *http.Request) {
|
func trainingListHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
log.Println("Handling training list request")
|
||||||
trainings, err := fetchTrainings()
|
trainings, err := fetchTrainings()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Printf("Error fetching trainings: %v", err)
|
||||||
http.Error(w, "Database error", http.StatusInternalServerError)
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = tmpl.ExecuteTemplate(w, "training_list.html", trainings)
|
err = tmpl.ExecuteTemplate(w, "training_list.html", trainings)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Printf("Error in templating page training_list.html: %v", err)
|
||||||
http.Error(w, fmt.Sprintf("Error in templating page %v", err), http.StatusInternalServerError)
|
http.Error(w, fmt.Sprintf("Error in templating page %v", err), http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchTrainers() ([]Trainer, error) {
|
func fetchTrainers() ([]Trainer, error) {
|
||||||
|
log.Println("Fetching trainers from the database")
|
||||||
rows, err := db.Query("SELECT id, name, email FROM trainers")
|
rows, err := db.Query("SELECT id, name, email FROM trainers")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Printf("Error fetching trainers: %v", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
@ -257,52 +297,63 @@ func fetchTrainers() ([]Trainer, error) {
|
||||||
var t Trainer
|
var t Trainer
|
||||||
err := rows.Scan(&t.ID, &t.Name, &t.Email)
|
err := rows.Scan(&t.ID, &t.Name, &t.Email)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Printf("Error scanning row for trainer: %v", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
trainers = append(trainers, t)
|
trainers = append(trainers, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Printf("Found %d trainers", len(trainers))
|
||||||
return trainers, nil
|
return trainers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func trainerListHandler(w http.ResponseWriter, r *http.Request) {
|
func trainerListHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
log.Println("Handling trainer list request")
|
||||||
trainers, err := fetchTrainers()
|
trainers, err := fetchTrainers()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Printf("Error fetching trainers: %v", err)
|
||||||
http.Error(w, "Database error", http.StatusInternalServerError)
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = tmpl.ExecuteTemplate(w, "trainer_list.html", trainers)
|
err = tmpl.ExecuteTemplate(w, "trainer_list.html", trainers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Printf("Error in templating page trainer_list.html: %v", err)
|
||||||
http.Error(w, fmt.Sprintf("Error in templating page %v", err), http.StatusInternalServerError)
|
http.Error(w, fmt.Sprintf("Error in templating page %v", err), http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func addTrainerHandler(w http.ResponseWriter, r *http.Request) {
|
func addTrainerHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
log.Println("Handling request to add a new trainer")
|
||||||
if r.Method == http.MethodPost {
|
if r.Method == http.MethodPost {
|
||||||
name := r.FormValue("name")
|
name := r.FormValue("name")
|
||||||
email := r.FormValue("email")
|
email := r.FormValue("email")
|
||||||
if name == "" || email == "" {
|
if name == "" || email == "" {
|
||||||
|
log.Println("Trainer name and email are required")
|
||||||
http.Error(w, "Name and Email are required", http.StatusBadRequest)
|
http.Error(w, "Name and Email are required", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := db.Exec("INSERT INTO trainers (name, email) VALUES (?, ?)", name, email)
|
_, err := db.Exec("INSERT INTO trainers (name, email) VALUES (?, ?)", name, email)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Printf("Error adding trainer: %v", err)
|
||||||
http.Error(w, "Database error", http.StatusInternalServerError)
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
log.Println("New trainer added successfully")
|
||||||
http.Redirect(w, r, "/trainers", http.StatusSeeOther)
|
http.Redirect(w, r, "/trainers", http.StatusSeeOther)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err := tmpl.ExecuteTemplate(w, "add_trainer.html", nil)
|
err := tmpl.ExecuteTemplate(w, "add_trainer.html", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Printf("Error in templating page add_trainer.html: %v", err)
|
||||||
http.Error(w, fmt.Sprintf("Error in templating page %v", err), http.StatusInternalServerError)
|
http.Error(w, fmt.Sprintf("Error in templating page %v", err), http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func hashPassword(password string) (string, error) {
|
func hashPassword(password string) (string, error) {
|
||||||
|
log.Println("Hashing password")
|
||||||
bytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
bytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||||
return string(bytes), err
|
return string(bytes), err
|
||||||
}
|
}
|
||||||
|
|
@ -367,13 +418,114 @@ func redirectIfNoAdmin(next http.Handler) http.Handler {
|
||||||
http.Redirect(w, r, "/setup-admin", http.StatusSeeOther)
|
http.Redirect(w, r, "/setup-admin", http.StatusSeeOther)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
session, err := store.Get(r, "user-session")
|
||||||
|
if err != nil || session.Values["username"] == nil {
|
||||||
|
http.Redirect(w, r, "/login", http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func checkPasswordHash(password, hash string) bool {
|
||||||
initDB()
|
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
|
||||||
initTemplates()
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// / loginHandler handles the user login
|
||||||
|
func loginHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method == http.MethodGet {
|
||||||
|
// Show the login page
|
||||||
|
log.Println("Rendering login page.")
|
||||||
|
err := tmpl.ExecuteTemplate(w, "login.html", nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Error rendering login template:", err)
|
||||||
|
http.Error(w, "Template rendering error", http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Method == http.MethodPost {
|
||||||
|
username := r.FormValue("username")
|
||||||
|
password := r.FormValue("password")
|
||||||
|
|
||||||
|
log.Printf("Received login attempt for username: %s", username)
|
||||||
|
|
||||||
|
// Query the admin table for the password hash
|
||||||
|
var storedHash string
|
||||||
|
err := db.QueryRow("SELECT password FROM admins WHERE email = $1", username).Scan(&storedHash)
|
||||||
|
if err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
// User doesn't exist
|
||||||
|
log.Println("Invalid username or password: no matching user found.")
|
||||||
|
http.Error(w, "Invalid username or password", http.StatusUnauthorized)
|
||||||
|
} else {
|
||||||
|
// Database error
|
||||||
|
log.Println("Error querying database:", err)
|
||||||
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the provided password matches the stored hash
|
||||||
|
if !checkPasswordHash(password, storedHash) {
|
||||||
|
log.Println("Invalid username or password: password mismatch.")
|
||||||
|
http.Error(w, "Invalid username or password", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Password is correct; set up a session
|
||||||
|
session, err := store.Get(r, "user-session")
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Error creating session:", err)
|
||||||
|
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store username in the session
|
||||||
|
session.Values["username"] = username
|
||||||
|
err = session.Save(r, w)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Error saving session:", err)
|
||||||
|
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("User '%s' successfully logged in.", username)
|
||||||
|
|
||||||
|
// Redirect to the home page (or dashboard)
|
||||||
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// logoutHandler logs the user out by destroying their session
|
||||||
|
func logoutHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
log.Println("Logging out user.")
|
||||||
|
session, err := store.Get(r, "user-session")
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Error fetching session:", err)
|
||||||
|
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear the session
|
||||||
|
session.Options.MaxAge = -1
|
||||||
|
err = session.Save(r, w)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Error saving session:", err)
|
||||||
|
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("User successfully logged out.")
|
||||||
|
|
||||||
|
// Redirect to the login page or home page
|
||||||
|
http.Redirect(w, r, "/login", http.StatusSeeOther)
|
||||||
|
}
|
||||||
|
|
||||||
|
func initServerHandlers() {
|
||||||
http.Handle("/", redirectIfNoAdmin(http.HandlerFunc(homeHandler)))
|
http.Handle("/", redirectIfNoAdmin(http.HandlerFunc(homeHandler)))
|
||||||
http.Handle("/trainings", redirectIfNoAdmin(http.HandlerFunc(trainingListHandler)))
|
http.Handle("/trainings", redirectIfNoAdmin(http.HandlerFunc(trainingListHandler)))
|
||||||
http.Handle("/trainers", redirectIfNoAdmin(http.HandlerFunc(trainerListHandler)))
|
http.Handle("/trainers", redirectIfNoAdmin(http.HandlerFunc(trainerListHandler)))
|
||||||
|
|
@ -383,7 +535,18 @@ func main() {
|
||||||
http.Handle("/training-areas/assign-trainer", redirectIfNoAdmin(http.HandlerFunc(assignTrainerToAreaHandler)))
|
http.Handle("/training-areas/assign-trainer", redirectIfNoAdmin(http.HandlerFunc(assignTrainerToAreaHandler)))
|
||||||
|
|
||||||
http.HandleFunc("/setup-admin", setupAdminHandler)
|
http.HandleFunc("/setup-admin", setupAdminHandler)
|
||||||
|
http.HandleFunc("/login", loginHandler)
|
||||||
fmt.Println("Server running on http://localhost:8080")
|
http.HandleFunc("/logout", logoutHandler)
|
||||||
http.ListenAndServe(":8080", nil)
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
initDB()
|
||||||
|
initTemplates()
|
||||||
|
initServerHandlers()
|
||||||
|
|
||||||
|
log.Println("Starting server on :8080")
|
||||||
|
err := http.ListenAndServe(":8080", nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error starting server: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
18
templates/login.html
Normal file
18
templates/login.html
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
{{template "head.html" .}} <!-- Include header template -->
|
||||||
|
|
||||||
|
<body>
|
||||||
|
{{template "header.html" .}}
|
||||||
|
<h2>Please log in to continue</h2>
|
||||||
|
<form action="/login" method="POST">
|
||||||
|
<label for="username">Username:</label>
|
||||||
|
<input type="text" id="username" name="username" required><br><br>
|
||||||
|
|
||||||
|
<label for="password">Password:</label>
|
||||||
|
<input type="password" id="password" name="password" required><br><br>
|
||||||
|
|
||||||
|
<button type="submit">Login</button>
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Loading…
Add table
Reference in a new issue