From 14283c2b61c22d663ffcf54cf8661e5456616c75 Mon Sep 17 00:00:00 2001 From: Yovan Date: Sat, 3 Oct 2026 19:23:22 +0700 Subject: [PATCH] tabel user --- cmd/server/main.go | 2 +- internal/handler/auth_handler.go | 13 ++++++++----- internal/handler/snippet_handler.go | 29 ++++++++++++++++++++++++++++- internal/store/snippet_store.go | 25 +++++++++++++++++++++++++ internal/store/user_store.go | 14 +++++++++++++- 5 files changed, 75 insertions(+), 8 deletions(-) diff --git a/cmd/server/main.go b/cmd/server/main.go index 9983125..252863b 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -49,7 +49,7 @@ func main() { mux.Handle("GET /snippets", authMiddleware.RequireAuth(http.HandlerFunc(snippetHandler.List))) mux.Handle("POST /snippets", authMiddleware.RequireAuth(http.HandlerFunc(snippetHandler.Create))) - mux.Handle("GET /snippets/{id}", authMiddleware.RequireAuth(http.HandlerFunc(snippetHandler.Get))) + mux.HandleFunc("GET /snippets/{id}", snippetHandler.Get) mux.Handle("DELETE /snippets/{id}", authMiddleware.RequireAuth(http.HandlerFunc(snippetHandler.Delete))) port := os.Getenv("PORT") diff --git a/internal/handler/auth_handler.go b/internal/handler/auth_handler.go index f0d6822..bfa7a4a 100644 --- a/internal/handler/auth_handler.go +++ b/internal/handler/auth_handler.go @@ -2,6 +2,7 @@ package handler import ( "encoding/json" + "errors" "net/http" "strings" "time" @@ -60,7 +61,11 @@ func (h *AuthHandler) Register(w http.ResponseWriter, r *http.Request) { } if err := h.userStore.Create(r.Context(), user); err != nil { - http.Error(w, "Username already exists or create failed", http.StatusBadRequest) + if errors.Is(err, store.ErrUsernameTaken) { + http.Error(w, "Username already exists", http.StatusConflict) + return + } + http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } @@ -106,7 +111,7 @@ func (h *AuthHandler) Login(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - _ = json.NewEncoder(w).Encode(session) + _ = json.NewEncoder(w).Encode(map[string]string{"session_token": session.Token}) } func (h *AuthHandler) Logout(w http.ResponseWriter, r *http.Request) { @@ -136,7 +141,5 @@ func (h *AuthHandler) Logout(w http.ResponseWriter, r *http.Request) { return } - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - _ = json.NewEncoder(w).Encode(map[string]string{"message": "logged out"}) + w.WriteHeader(http.StatusNoContent) } diff --git a/internal/handler/snippet_handler.go b/internal/handler/snippet_handler.go index c0021c5..390c961 100644 --- a/internal/handler/snippet_handler.go +++ b/internal/handler/snippet_handler.go @@ -96,7 +96,13 @@ func (h *SnippetHandler) List(w http.ResponseWriter, r *http.Request) { return } - snippets, err := h.snippetStore.GetAll(r.Context()) + userID := middleware.GetUserID(r.Context()) + if userID == "" { + http.Error(w, "Unauthorized", http.StatusUnauthorized) + return + } + + snippets, err := h.snippetStore.GetByUserID(r.Context(), userID) if err != nil { http.Error(w, "Internal Server Error", http.StatusInternalServerError) return @@ -145,6 +151,12 @@ func (h *SnippetHandler) Delete(w http.ResponseWriter, r *http.Request) { return } + userID := middleware.GetUserID(r.Context()) + if userID == "" { + http.Error(w, "Unauthorized", http.StatusUnauthorized) + return + } + id := r.PathValue("id") if id == "" { path := strings.TrimPrefix(r.URL.Path, "/snippets/") @@ -156,6 +168,21 @@ func (h *SnippetHandler) Delete(w http.ResponseWriter, r *http.Request) { return } + snippet, err := h.snippetStore.GetByID(r.Context(), id) + if err != nil { + if errors.Is(err, pgx.ErrNoRows) { + http.Error(w, "Snippet not found", http.StatusNotFound) + return + } + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + + if snippet.UserID != userID { + http.Error(w, "Forbidden", http.StatusForbidden) + return + } + if err := h.snippetStore.Delete(r.Context(), id); err != nil { http.Error(w, "Internal Server Error", http.StatusInternalServerError) return diff --git a/internal/store/snippet_store.go b/internal/store/snippet_store.go index 5d54f98..bfa0cca 100644 --- a/internal/store/snippet_store.go +++ b/internal/store/snippet_store.go @@ -50,6 +50,31 @@ func (s *SnippetStore) GetByID(ctx context.Context, id string) (*models.Snippet, return &sn, nil } +func (s *SnippetStore) GetByUserID(ctx context.Context, userID string) ([]*models.Snippet, error) { + s.mu.Lock() + defer s.mu.Unlock() + + query := `SELECT id, title, content, user_id FROM snippets WHERE user_id = $1` + rows, err := s.conn.Query(ctx, query, userID) + if err != nil { + return nil, err + } + defer rows.Close() + + snippets := make([]*models.Snippet, 0) + for rows.Next() { + var sn models.Snippet + if err := rows.Scan(&sn.ID, &sn.Title, &sn.Content, &sn.UserID); err != nil { + return nil, err + } + snippets = append(snippets, &sn) + } + if err := rows.Err(); err != nil { + return nil, err + } + return snippets, nil +} + func (s *SnippetStore) GetAll(ctx context.Context) ([]*models.Snippet, error) { s.mu.Lock() defer s.mu.Unlock() diff --git a/internal/store/user_store.go b/internal/store/user_store.go index f910c7d..a659f72 100644 --- a/internal/store/user_store.go +++ b/internal/store/user_store.go @@ -2,13 +2,18 @@ package store import ( "context" + "errors" "sync" "5803025020/w04/internal/models" "github.com/google/uuid" "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgconn" ) +// ErrUsernameTaken dikembalikan saat Create() gagal karena username sudah dipakai (unique constraint). +var ErrUsernameTaken = errors.New("username already exists") + type UserStore struct { conn *pgx.Conn mu *sync.Mutex @@ -34,7 +39,14 @@ func (s *UserStore) Create(ctx context.Context, user *models.User) error { query := `INSERT INTO users (id, username, password_hash) VALUES ($1, $2, $3)` _, err := s.conn.Exec(ctx, query, user.ID, user.Username, user.PasswordHash) - return err + if err != nil { + var pgErr *pgconn.PgError + if errors.As(err, &pgErr) && pgErr.Code == "23505" { + return ErrUsernameTaken + } + return err + } + return nil } func (s *UserStore) GetByUsername(ctx context.Context, username string) (*models.User, error) {