2024-09-22 23:57:23 +00:00
|
|
|
package api
|
|
|
|
|
|
|
|
import (
|
2024-11-01 21:03:08 +00:00
|
|
|
"arimelody-web/controller"
|
|
|
|
"arimelody-web/model"
|
2024-09-22 23:57:23 +00:00
|
|
|
"arimelody-web/global"
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"golang.org/x/crypto/bcrypt"
|
|
|
|
)
|
|
|
|
|
|
|
|
func handleLogin() http.HandlerFunc {
|
|
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if r.Method != http.MethodPost {
|
|
|
|
http.NotFound(w, r)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
type LoginRequest struct {
|
|
|
|
Username string `json:"username"`
|
|
|
|
Password string `json:"password"`
|
|
|
|
}
|
|
|
|
|
|
|
|
credentials := LoginRequest{}
|
|
|
|
err := json.NewDecoder(r.Body).Decode(&credentials)
|
|
|
|
if err != nil {
|
|
|
|
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
account, err := controller.GetAccount(global.DB, credentials.Username)
|
|
|
|
if err != nil {
|
|
|
|
if strings.Contains(err.Error(), "no rows") {
|
|
|
|
http.Error(w, "Invalid username or password", http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
fmt.Fprintf(os.Stderr, "WARN: Failed to retrieve account: %s\n", err.Error())
|
|
|
|
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
err = bcrypt.CompareHashAndPassword(account.Password, []byte(credentials.Password))
|
|
|
|
if err != nil {
|
|
|
|
http.Error(w, "Invalid username or password", http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: sessions and tokens
|
|
|
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
w.Write([]byte("Logged in successfully. TODO: Session tokens\n"))
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func handleAccountRegistration() http.HandlerFunc {
|
|
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if r.Method != http.MethodPost {
|
|
|
|
http.NotFound(w, r)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
type RegisterRequest struct {
|
|
|
|
Username string `json:"username"`
|
|
|
|
Email string `json:"email"`
|
|
|
|
Password string `json:"password"`
|
|
|
|
Code string `json:"code"`
|
|
|
|
}
|
|
|
|
|
|
|
|
credentials := RegisterRequest{}
|
|
|
|
err := json.NewDecoder(r.Body).Decode(&credentials)
|
|
|
|
if err != nil {
|
|
|
|
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// make sure code exists in DB
|
|
|
|
invite := model.Invite{}
|
|
|
|
err = global.DB.Get(&invite, "SELECT * FROM invite WHERE code=$1", credentials.Code)
|
|
|
|
if err != nil {
|
|
|
|
if strings.Contains(err.Error(), "no rows") {
|
|
|
|
http.Error(w, "Invalid invite code", http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
fmt.Fprintf(os.Stderr, "WARN: Failed to retrieve invite: %s\n", err.Error())
|
|
|
|
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if time.Now().After(invite.ExpiresAt) {
|
|
|
|
http.Error(w, "Invalid invite code", http.StatusBadRequest)
|
|
|
|
_, err = global.DB.Exec("DELETE FROM invite WHERE code=$1", credentials.Code)
|
|
|
|
if err != nil { fmt.Fprintf(os.Stderr, "WARN: Failed to delete expired invite: %s\n", err.Error()) }
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(credentials.Password), bcrypt.DefaultCost)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Fprintf(os.Stderr, "WARN: Failed to generate password hash: %s\n", err.Error())
|
|
|
|
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
account := model.Account{
|
|
|
|
Username: credentials.Username,
|
|
|
|
Password: hashedPassword,
|
|
|
|
Email: credentials.Email,
|
|
|
|
AvatarURL: "/img/default-avatar.png",
|
|
|
|
}
|
|
|
|
err = controller.CreateAccount(global.DB, &account)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Fprintf(os.Stderr, "WARN: Failed to create account: %s\n", err.Error())
|
|
|
|
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = global.DB.Exec("DELETE FROM invite WHERE code=$1", credentials.Code)
|
|
|
|
if err != nil { fmt.Fprintf(os.Stderr, "WARN: Failed to delete expired invite: %s\n", err.Error()) }
|
|
|
|
|
|
|
|
w.WriteHeader(http.StatusCreated)
|
|
|
|
w.Write([]byte("Account created successfully\n"))
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func handleDeleteAccount() http.HandlerFunc {
|
|
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if r.Method != http.MethodPost {
|
|
|
|
http.NotFound(w, r)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
type LoginRequest struct {
|
|
|
|
Username string `json:"username"`
|
|
|
|
Password string `json:"password"`
|
|
|
|
}
|
|
|
|
|
|
|
|
credentials := LoginRequest{}
|
|
|
|
err := json.NewDecoder(r.Body).Decode(&credentials)
|
|
|
|
if err != nil {
|
|
|
|
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
account, err := controller.GetAccount(global.DB, credentials.Username)
|
|
|
|
if err != nil {
|
|
|
|
if strings.Contains(err.Error(), "no rows") {
|
|
|
|
http.Error(w, "Invalid username or password", http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
fmt.Fprintf(os.Stderr, "WARN: Failed to retrieve account: %s\n", err.Error())
|
|
|
|
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
err = bcrypt.CompareHashAndPassword(account.Password, []byte(credentials.Password))
|
|
|
|
if err != nil {
|
|
|
|
http.Error(w, "Invalid username or password", http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
err = controller.DeleteAccount(global.DB, account.ID)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Fprintf(os.Stderr, "WARN: Failed to delete account: %s\n", err.Error())
|
|
|
|
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
w.Write([]byte("Account deleted successfully\n"))
|
|
|
|
})
|
|
|
|
}
|