From 5531ef6bab6b55bed292705ebe60e7fb794f907f Mon Sep 17 00:00:00 2001 From: ari melody Date: Tue, 21 Jan 2025 15:08:59 +0000 Subject: [PATCH] remove account API endpoints account management should be done on the frontend. some work will need to be done to generate API keys for external clients, but notably some API endpoints are currently used by the frontend using session tokens. --- api/account.go | 201 ------------------------------------------------- api/api.go | 12 --- 2 files changed, 213 deletions(-) delete mode 100644 api/account.go diff --git a/api/account.go b/api/account.go deleted file mode 100644 index 0a9a7f9..0000000 --- a/api/account.go +++ /dev/null @@ -1,201 +0,0 @@ -package api - -import ( - "arimelody-web/controller" - "arimelody-web/model" - "encoding/json" - "fmt" - "net/http" - "os" - "strings" - "time" - - "golang.org/x/crypto/bcrypt" -) - -func handleLogin(app *model.AppState) 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(app.DB, credentials.Username) - if err != nil { - fmt.Fprintf(os.Stderr, "WARN: Failed to retrieve account: %v\n", err) - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - return - } - if account == nil { - http.Error(w, "Invalid username or password", http.StatusBadRequest) - return - } - - err = bcrypt.CompareHashAndPassword([]byte(account.Password), []byte(credentials.Password)) - if err != nil { - http.Error(w, "Invalid username or password", http.StatusBadRequest) - return - } - - token, err := controller.CreateToken(app.DB, account.ID, r.UserAgent()) - type LoginResponse struct { - Token string `json:"token"` - ExpiresAt time.Time `json:"expires_at"` - } - - err = json.NewEncoder(w).Encode(LoginResponse{ - Token: token.Token, - ExpiresAt: token.ExpiresAt, - }) - if err != nil { - fmt.Fprintf(os.Stderr, "WARN: Failed to return session token: %v\n", err) - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - } - }) -} - -func handleAccountRegistration(app *model.AppState) 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"` - Invite string `json:"invite"` - } - - 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, err := controller.GetInvite(app.DB, credentials.Invite) - if err != nil { - fmt.Fprintf(os.Stderr, "WARN: Failed to retrieve invite: %v\n", err) - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - return - } - if invite == nil { - http.Error(w, "Invalid invite code", http.StatusBadRequest) - return - } - - if time.Now().After(invite.ExpiresAt) { - err := controller.DeleteInvite(app.DB, invite.Code) - if err != nil { fmt.Fprintf(os.Stderr, "WARN: Failed to delete expired invite: %v\n", err) } - http.Error(w, "Invalid invite code", http.StatusBadRequest) - return - } - - hashedPassword, err := bcrypt.GenerateFromPassword([]byte(credentials.Password), bcrypt.DefaultCost) - if err != nil { - fmt.Fprintf(os.Stderr, "WARN: Failed to generate password hash: %v\n", err) - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - return - } - - account := model.Account{ - Username: credentials.Username, - Password: string(hashedPassword), - Email: credentials.Email, - AvatarURL: "/img/default-avatar.png", - } - err = controller.CreateAccount(app.DB, &account) - if err != nil { - if strings.HasPrefix(err.Error(), "pq: duplicate key") { - http.Error(w, "An account with that username already exists", http.StatusBadRequest) - return - } - fmt.Fprintf(os.Stderr, "WARN: Failed to create account: %v\n", err) - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - return - } - - err = controller.DeleteInvite(app.DB, invite.Code) - if err != nil { fmt.Fprintf(os.Stderr, "WARN: Failed to delete expired invite: %v\n", err) } - - token, err := controller.CreateToken(app.DB, account.ID, r.UserAgent()) - type LoginResponse struct { - Token string `json:"token"` - ExpiresAt time.Time `json:"expires_at"` - } - - err = json.NewEncoder(w).Encode(LoginResponse{ - Token: token.Token, - ExpiresAt: token.ExpiresAt, - }) - if err != nil { - fmt.Fprintf(os.Stderr, "WARN: Failed to return session token: %v\n", err) - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - } - }) -} - -func handleDeleteAccount(app *model.AppState) 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(app.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: %v\n", err) - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - return - } - - err = bcrypt.CompareHashAndPassword([]byte(account.Password), []byte(credentials.Password)) - if err != nil { - http.Error(w, "Invalid password", http.StatusBadRequest) - return - } - - // TODO: check TOTP - - err = controller.DeleteAccount(app.DB, account.Username) - if err != nil { - fmt.Fprintf(os.Stderr, "WARN: Failed to delete account: %v\n", err) - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - return - } - - w.WriteHeader(http.StatusOK) - w.Write([]byte("Account deleted successfully\n")) - }) -} diff --git a/api/api.go b/api/api.go index b16d45d..9489126 100644 --- a/api/api.go +++ b/api/api.go @@ -13,20 +13,8 @@ import ( func Handler(app *model.AppState) http.Handler { mux := http.NewServeMux() - // ACCOUNT ENDPOINTS - - /* - // temporarily disabling these - // accounts should really be handled via the frontend rn, and juggling - // two different token bearer methods kinda sucks!! - // i'll look into generating API tokens on the frontend in the future // TODO: generate API keys on the frontend - mux.Handle("/v1/login", handleLogin()) - mux.Handle("/v1/register", handleAccountRegistration()) - mux.Handle("/v1/delete-account", handleDeleteAccount()) - */ - // ARTIST ENDPOINTS mux.Handle("/v1/artist/", http.StripPrefix("/v1/artist", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {