arimelody.me/api/account.go

176 lines
6.2 KiB
Go

package api
import (
"arimelody-web/account/controller"
"arimelody-web/account/model"
"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"))
})
}