diff --git a/main.go b/main.go index 852a78145b0e4bb484263550b0ac254b2b3183be..efa8205ebe693a74d6f8151a2895689a7c0fae23 100644 --- a/main.go +++ b/main.go @@ -15,6 +15,8 @@ import ( "strconv" "time" "github.com/go-gomail/gomail" + "io" + "mime/multipart" ) var usersCollection *mongo.Collection @@ -23,7 +25,10 @@ var URI string var messages []models.Post var err error var user models.User +var userUpdated models.User var username_global string +var globalAvatarURL string = "https://mstd.dansmonorage.blue/system/site_uploads/files/000/000/002/original/mascot.png" +var admins []models.User // Session represents a user session. type Session struct { @@ -70,6 +75,130 @@ func sendPasswordResetEmail(email, token string) error { return d.DialAndSend(m) } +func getAllUsers() ([]models.User, error) { + // Connect to the MongoDB database + clientOptions := options.Client().ApplyURI(URI) + client, err := mongo.Connect(context.Background(), clientOptions) + if err != nil { + return nil, err + } + defer client.Disconnect(context.Background()) + + // Retrieve all users from the database + var users []models.User + cursor, err := usersCollection.Find(context.Background(), bson.M{}) + if err != nil { + return nil, err + } + defer cursor.Close(context.Background()) + for cursor.Next(context.Background()) { + var user models.User + err := cursor.Decode(&user) + if err != nil { + return nil, err + } + users = append(users, user) + } + + return users, nil +} + +func adminHandler(w http.ResponseWriter, r *http.Request) { + // Check if the user is an admin + cookie, err := r.Cookie("username") + if err != nil { + http.Redirect(w, r, "/signin", http.StatusSeeOther) + return + } + username := cookie.Value + user, err := getUser(username) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if user.Role != "admin" { + http.Error(w, "Unauthorized", http.StatusUnauthorized) + return + } + + // Retrieve all users from the database + users, err := getAllUsers() + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + // Filter the users to only include admins + var admins []models.User + for _, user := range users { + if user.Role != "admin" { + admins = append(admins, user) + } + } + + // Render the admin page with the list of admins + tmpl, err := template.ParseFiles("./view/admin.html") + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + err = tmpl.Execute(w, models.AdminTemplate{ + Users: admins, + }) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} + +func adminHandler_old(w http.ResponseWriter, r *http.Request) { + // Check if the user is an admin + cookie, err := r.Cookie("username") + if err != nil { + http.Redirect(w, r, "/signin", http.StatusSeeOther) + return + } + username := cookie.Value + user, err := getUser(username) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + fmt.Println("printing user role") + fmt.Println(user.Role) + if user.Role != "admin" { + http.Error(w, "Unauthorized", http.StatusUnauthorized) + return + } + + // Retrieve all users from the database + users, err := getAllUsers() + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + // Render the admin page with the list of users + tmpl, err := template.ParseFiles("./view/admin.html") + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + // Filter the users to only include admins + for _, user := range users { + if user.Role == "admin" { + admins = append(admins, user) + } + } + + // Pass the filtered list of admins to the template + err = tmpl.Execute(w, admins) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} // Define a function to handle the POST request func handleMessage(w http.ResponseWriter, r *http.Request) { @@ -126,6 +255,76 @@ func handleMessage(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, "/profile", http.StatusTemporaryRedirect) } +func updateUserAvatar(username, avatarURL string) (models.User, error) { + // Connect to the MongoDB database + clientOptions := options.Client().ApplyURI(URI) + client, err := mongo.Connect(context.Background(), clientOptions) + if err != nil { + return models.User{}, err + } + defer client.Disconnect(context.Background()) + + // Update the user's avatar URL in the database + _, err = usersCollection.UpdateOne(context.Background(), bson.M{"username": username}, bson.M{"$set": bson.M{"avatar": avatarURL}}) + if err != nil { + return models.User{}, err + } + + user, err:= getUser(username) + if(err != nil) { + return models.User{}, err + } + user.Avatar = avatarURL + data.Avatar = avatarURL + globalAvatarURL = avatarURL + data.GlobalAvatarURL = avatarURL + + return user, nil +} + +func uploadAvatarHandler(w http.ResponseWriter, r *http.Request) { + // Get the user's username from the cookie + cookie, err := r.Cookie("username") + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + username := cookie.Value + + // Retrieve the uploaded image data + file, _, err := r.FormFile("avatar") + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + defer file.Close() + + // Save the uploaded image to disk + filename := fmt.Sprintf("%s.jpg", username) + err = os.MkdirAll("./uploads", os.ModePerm) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + f, err := os.OpenFile("./uploads/"+filename, os.O_WRONLY|os.O_CREATE, 0666) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + defer f.Close() + io.Copy(f, file) + + // Update the user's avatar URL in the database + userUpdated, err = updateUserAvatar(username, "/uploads/"+filename) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + // Redirect the user back to their profile page + http.Redirect(w, r, "/profile", http.StatusTemporaryRedirect) +} + func main() { // Initialize the sessions map sessions = make(map[string]Session) @@ -150,9 +349,65 @@ func main() { http.HandleFunc("/profile", successSignin) http.HandleFunc("/reset-password", handlePasswordReset) http.HandleFunc("/post", handlePost) + http.HandleFunc("/messages", messagesHandler) + http.HandleFunc("/edit", editHandler) + http.HandleFunc("/admin", adminHandler) + http.HandleFunc("/delete", deleteHandler) log.Fatal(http.ListenAndServe(":5500", nil)) } + +//deleteUser function +func deleteUser(username string) error { + // Connect to the MongoDB database + clientOptions := options.Client().ApplyURI(URI) + client, err := mongo.Connect(context.Background(), clientOptions) + if err != nil { + return err + } + defer client.Disconnect(context.Background()) + + // Delete the user from the database + _, err = usersCollection.DeleteOne(context.Background(), bson.M{"username": username}) + if err != nil { + return err + } + + return nil +} + +func deleteHandler(w http.ResponseWriter, r *http.Request) { + // Check if the user is an admin + cookie, err := r.Cookie("username") + if err != nil { + http.Redirect(w, r, "/signin", http.StatusSeeOther) + return + } + username := cookie.Value + user, err := getUser(username) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if user.Role != "admin" { + http.Error(w, "Unauthorized", http.StatusUnauthorized) + return + } + + // Get the username to delete from the form data + usernameToDelete := r.FormValue("username") + + // Delete the user from the database + err = deleteUser(usernameToDelete) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + // Redirect the user back to the admin page + http.Redirect(w, r, "/admin", http.StatusSeeOther) +} + //getCurrentUser function func getCurrentUser(r *http.Request) models.User { return user @@ -268,6 +523,7 @@ func signUpHandler(w http.ResponseWriter, r *http.Request) { Age: age, Bio: bio, Role: admin, + Avatar: "https://mstd.dansmonorage.blue/system/site_uploads/files/000/000/002/original/mascot.png", } var search models.User if usersCollection.FindOne(context.TODO(), bson.M{"username": username}).Decode(&search); search.Username == username { @@ -299,11 +555,24 @@ func successSignin(w http.ResponseWriter, r *http.Request) { return } + cookie := http.Cookie{ + Name: "username", + Value: username_global, + Expires: time.Now().Add(24 * time.Hour), + } + http.SetCookie(w, &cookie) + + username := cookie.Value + fmt.Println("printing username from cookie value") + fmt.Println(username) + err = tmpl.Execute(w, data) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } + + // Get the username from the database //var user models.User //err = usersCollection.FindOne(context.Background(), bson.M{"username": r.FormValue("username")}).Decode(&user) @@ -315,7 +584,6 @@ func successSignin(w http.ResponseWriter, r *http.Request) { if messageContent != "" { // Create a new message object message := models.Post{ - //From: user.Username, Username: username_global, Title: r.FormValue("title"), CreatedAt: time.Now(), @@ -345,29 +613,183 @@ func successSignin(w http.ResponseWriter, r *http.Request) { return } fmt.Println(messages) - // Render the profile page with the user's messages - tmpl, err := template.ParseFiles("./view/profile.html") + +} + +// getUser retrieves a user document from the database. +func getUser(username string) (models.User, error) { + // Connect to the MongoDB database + clientOptions := options.Client().ApplyURI(URI) + client, err := mongo.Connect(context.Background(), clientOptions) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return + return models.User{}, err } - err = tmpl.Execute(w, struct { - User models.User - Messages []models.Post - FullName string - Email string - }{ - User: user, - Messages: messages, - FullName: user.FullName, - Email: user.Email, - }) + defer client.Disconnect(context.Background()) + + // Retrieve the user document from the database + var user models.User + err = usersCollection.FindOne(context.Background(), bson.M{"username": username}).Decode(&user) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return + return models.User{}, err + } + + return user, nil +} + +//updateUser updates a user document in the database. +func updateUser(user models.User) error { + // Connect to the MongoDB database + clientOptions := options.Client().ApplyURI(URI) + client, err := mongo.Connect(context.Background(), clientOptions) + if err != nil { + return err + } + defer client.Disconnect(context.Background()) + + // Update the user in the database + _, err = usersCollection.UpdateOne(context.Background(), bson.M{"username": user.Username}, bson.M{"$set": user}) + if err != nil { + return err + } + + return nil +} + +// saveAvatarFile saves an avatar file to disk. +func saveAvatarFile(username, filename string, file multipart.File) error { + // Create the uploads directory if it does not already exist + err := os.MkdirAll("./uploads", os.ModePerm) + if err != nil { + return err + } + + // Create a new file in the uploads directory + f, err := os.OpenFile("./uploads/"+username+"_"+filename, os.O_WRONLY|os.O_CREATE, 0666) + if err != nil { + return err + } + defer f.Close() + + // Copy the uploaded file to the new file + _, err = io.Copy(f, file) + if err != nil { + return err } + return nil +} + + +func editHandler(w http.ResponseWriter, r *http.Request) { + // Get the user's username from the cookie + cookie, err := r.Cookie("username") + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + username := cookie.Value + fmt.Println("printing username from cookie value in editHandler") + fmt.Println(username) + + // Retrieve the user's information from the database + user, err := getUser(username) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + // Handle form submission + if r.Method == "POST" { + // Parse the form data + err := r.ParseMultipartForm(32 << 20) // 32 MB + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + // Update the user's information in the database + user.FullName = r.FormValue("fullname") + user.Email = r.FormValue("email") + err = updateUser(user) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + // Upload the new avatar file (if provided) + file, handler, err := r.FormFile("avatar") + if err == nil { + defer file.Close() + + // Save the file to disk + err = saveAvatarFile(user.Username, handler.Filename, file) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + // Update the user's avatar URL in the database + user.Avatar = fmt.Sprintf("/avatars/%s/%s", user.Username, handler.Filename) + err = updateUser(user) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + } + + // Redirect the user to the profile page + http.Redirect(w, r, "/profile", http.StatusSeeOther) + return + } + + // Render the edit page with the user's information + tmpl, err := template.ParseFiles("./view/edit.html") + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + err = tmpl.Execute(w, struct { + User models.User + }{ + User: user, + }) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} + +func messagesHandler(w http.ResponseWriter, r *http.Request) { + // Get the user's username from the cookie + cookie, err := r.Cookie("username") + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + username := cookie.Value + + // Retrieve the user's messages from the database + messages, err := getUserMessages(username) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + // Render the messages page with the user's messages + tmpl, err := template.ParseFiles("./view/messages.html") + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + err = tmpl.Execute(w, struct { + Messages []models.Post + }{ + Messages: messages, + }) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } } func getUserMessages(username string) ([]models.Post, error) { diff --git a/models/page_templ.go b/models/page_templ.go index b0cac6a0be7190eacad2bbbd4415a27efb7fbc20..2696f003863c2f9982aa0eb07138758b17e4da51 100644 --- a/models/page_templ.go +++ b/models/page_templ.go @@ -10,4 +10,12 @@ type PageTemplate struct { UserID int Role string Messages []Post + Avatar string + User User + GlobalAvatarURL string +} + + +type AdminTemplate struct { + Users []User } \ No newline at end of file diff --git a/models/user.go b/models/user.go index 918ec26a7f2cfa246dca7b39b2c309bf29ea59f6..c0850a3c81c35a3eb435cd6b3cf236deba955ac9 100644 --- a/models/user.go +++ b/models/user.go @@ -19,4 +19,5 @@ type User struct { Bio string Role string Posts []Post + Avatar string `bson:"avatarURL"` } diff --git a/view/profile.html b/view/profile.html index 7b0fa657aabb78bf4b476ab5fe1e26dc38e0b719..727589aed683c6f4f088e36462f618964664e18a 100644 --- a/view/profile.html +++ b/view/profile.html @@ -103,11 +103,21 @@ <h2>Posts</h2> <ul> {{range .Messages}} - <li>{{.Content}} ({{.Timestamp}})</li> + <li>{{.Content}}</li> {{end}} </ul> + <a href="/messages">Messages</a> + <h2>Avatar</h2> + {{if .User.Avatar}} + <img src="/uploads/{{.User.Avatar}}" alt="avatar" class="profile-photo"> + {{else}} + <img src="https://mstd.dansmonorage.blue/system/site_uploads/files/000/000/002/original/mascot.png" alt="avatar" + class="profile-photo"> + {{end}} + <a href="/edit?username={{.User.Username}}">EditProfile</a> + + <a href="/admin">Admin</a> - <p>User ID: {{.UserID}}</p> </body> </html>