-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #29 from KonferCA/add-auth
Add auth
- Loading branch information
Showing
12 changed files
with
528 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
-- +goose Up | ||
-- +goose StatementBegin | ||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; | ||
|
||
CREATE TABLE users ( | ||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), | ||
email VARCHAR(255) UNIQUE NOT NULL, | ||
password_hash VARCHAR(255) NOT NULL, | ||
first_name VARCHAR(100), | ||
last_name VARCHAR(100), | ||
role VARCHAR(50) NOT NULL, | ||
wallet_address VARCHAR(100), | ||
created_at TIMESTAMP DEFAULT NOW(), | ||
updated_at TIMESTAMP DEFAULT NOW() | ||
); | ||
-- +goose StatementEnd | ||
|
||
-- +goose Down | ||
-- +goose StatementBegin | ||
DROP TABLE users; | ||
-- +goose StatementEnd |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
-- name: CreateUser :one | ||
INSERT INTO users ( | ||
email, | ||
password_hash, | ||
first_name, | ||
last_name, | ||
role | ||
) VALUES ( | ||
$1, $2, $3, $4, $5 | ||
) RETURNING *; | ||
|
||
-- name: GetUserByEmail :one | ||
SELECT * FROM users | ||
WHERE email = $1 LIMIT 1; | ||
|
||
-- name: GetUserByID :one | ||
SELECT * FROM users | ||
WHERE id = $1 LIMIT 1; |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
package server | ||
|
||
import ( | ||
"context" | ||
"net/http" | ||
|
||
"github.com/KonferCA/NoKap/db" | ||
"github.com/emicklei/pgtalk/convert" | ||
"github.com/jackc/pgx/v5/pgtype" | ||
"github.com/labstack/echo/v4" | ||
"golang.org/x/crypto/bcrypt" | ||
) | ||
|
||
func (s *Server) setupAuthRoutes() { | ||
auth := s.apiV1.Group("/auth") | ||
auth.POST("/signup", s.handleSignup) | ||
auth.POST("/signin", s.handleSignin) | ||
} | ||
|
||
func (s *Server) handleSignup(c echo.Context) error { | ||
var req SignupRequest | ||
if err := c.Bind(&req); err != nil { | ||
return echo.NewHTTPError(http.StatusBadRequest, "invalid request body") | ||
} | ||
|
||
if err := c.Validate(&req); err != nil { | ||
return err | ||
} | ||
|
||
ctx := context.Background() | ||
existingUser, err := s.queries.GetUserByEmail(ctx, req.Email) | ||
if err == nil && existingUser.ID.Valid { | ||
return echo.NewHTTPError(http.StatusConflict, "email already registered") | ||
} | ||
|
||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost) | ||
if err != nil { | ||
return echo.NewHTTPError(http.StatusInternalServerError, "failed to hash password") | ||
} | ||
|
||
user, err := s.queries.CreateUser(ctx, db.CreateUserParams{ | ||
Email: req.Email, | ||
PasswordHash: string(hashedPassword), | ||
FirstName: pgtype.Text{String: req.FirstName, Valid: true}, | ||
LastName: pgtype.Text{String: req.LastName, Valid: true}, | ||
Role: req.Role, | ||
}) | ||
if err != nil { | ||
return echo.NewHTTPError(http.StatusInternalServerError, "failed to create user") | ||
} | ||
|
||
userID := convert.UUIDToString(user.ID) | ||
token, err := generateJWT(userID, user.Role) | ||
if err != nil { | ||
return echo.NewHTTPError(http.StatusInternalServerError, "failed to generate token") | ||
} | ||
|
||
return c.JSON(http.StatusCreated, AuthResponse{ | ||
Token: token, | ||
User: User{ | ||
ID: userID, | ||
Email: user.Email, | ||
FirstName: user.FirstName.String, | ||
LastName: user.LastName.String, | ||
Role: user.Role, | ||
WalletAddress: getStringPtr(user.WalletAddress), | ||
}, | ||
}) | ||
} | ||
|
||
func (s *Server) handleSignin(c echo.Context) error { | ||
var req SigninRequest | ||
if err := c.Bind(&req); err != nil { | ||
return echo.NewHTTPError(http.StatusBadRequest, "invalid request body") | ||
} | ||
|
||
if err := c.Validate(&req); err != nil { | ||
return err | ||
} | ||
|
||
ctx := context.Background() | ||
user, err := s.queries.GetUserByEmail(ctx, req.Email) | ||
if err != nil { | ||
return echo.NewHTTPError(http.StatusUnauthorized, "invalid credentials") | ||
} | ||
|
||
if err := bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(req.Password)); err != nil { | ||
return echo.NewHTTPError(http.StatusUnauthorized, "invalid credentials") | ||
} | ||
|
||
userID := convert.UUIDToString(user.ID) | ||
token, err := generateJWT(userID, user.Role) | ||
if err != nil { | ||
return echo.NewHTTPError(http.StatusInternalServerError, "failed to generate token") | ||
} | ||
|
||
return c.JSON(http.StatusOK, AuthResponse{ | ||
Token: token, | ||
User: User{ | ||
ID: userID, | ||
Email: user.Email, | ||
FirstName: user.FirstName.String, | ||
LastName: user.LastName.String, | ||
Role: user.Role, | ||
WalletAddress: getStringPtr(user.WalletAddress), | ||
}, | ||
}) | ||
} | ||
|
||
// helper function to convert pgtype.Text to *string | ||
func getStringPtr(t pgtype.Text) *string { | ||
if !t.Valid { | ||
return nil | ||
} | ||
return &t.String | ||
} |
Oops, something went wrong.