Skip to content

Commit

Permalink
add user handler, user view fix small mistake
Browse files Browse the repository at this point in the history
  • Loading branch information
IgorPolyakov committed Apr 19, 2024
1 parent fe45470 commit 1378419
Show file tree
Hide file tree
Showing 9 changed files with 127 additions and 37 deletions.
1 change: 0 additions & 1 deletion api/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,6 @@ paths:
content:
application/json:
schema:
# fixme тут нужно убрать id из запроса
$ref: '#/components/schemas/GameRequest'
required: true
responses:
Expand Down
7 changes: 4 additions & 3 deletions db/migration/init.sql
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
-- Пользователи
CREATE TABLE users (
id SERIAL PRIMARY KEY,
user_name VARCHAR(255) NOT NULL,
user_name VARCHAR(255) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
avatar_url VARCHAR(255),
role VARCHAR(255) NOT NULL CHECK (role IN ('admin', 'player'))
status VARCHAR(255),
role VARCHAR(255) NOT NULL CHECK (role IN ('admin', 'player', 'guest'))
);

-- Команды
CREATE TABLE teams (
id SERIAL PRIMARY KEY,
team_name VARCHAR(255) NOT NULL,
team_name VARCHAR(255) UNIQUE NOT NULL,
description VARCHAR(255) NOT NULL,
university VARCHAR(255),
social_links TEXT,
Expand Down
2 changes: 0 additions & 2 deletions src/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,12 @@ module ctf01d
go 1.22.2

require (
github.com/go-sql-driver/mysql v1.8.1
github.com/gorilla/mux v1.8.1
github.com/ilyakaznacheev/cleanenv v1.5.0
github.com/lib/pq v1.2.0
)

require (
filippo.io/edwards25519 v1.1.0 // indirect
github.com/BurntSushi/toml v1.2.1 // indirect
github.com/joho/godotenv v1.5.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
Expand Down
4 changes: 0 additions & 4 deletions src/go.sum
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/ilyakaznacheev/cleanenv v1.5.0 h1:0VNZXggJE2OYdXE87bfSSwGxeiGt9moSR2lOrsHHvr4=
Expand Down
4 changes: 2 additions & 2 deletions src/lib/api/games.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"net/http"
"time"

_ "github.com/go-sql-driver/mysql"
"github.com/gorilla/mux"
)

Expand All @@ -37,7 +36,7 @@ func CreateGameHandler(db *sql.DB, w http.ResponseWriter, r *http.Request) {
Description: game.Description,
}
if err := gameRepo.Create(r.Context(), newGame); err != nil {
respondWithJSON(w, http.StatusInternalServerError, map[string]string{"error": "Failed to create game"})
respondWithJSON(w, http.StatusInternalServerError, map[string]string{"error": "Failed to create game: " + err.Error()})
return
}
respondWithJSON(w, http.StatusOK, map[string]string{"data": "Game created successfully"})
Expand Down Expand Up @@ -100,6 +99,7 @@ func UpdateGameHandler(db *sql.DB, w http.ResponseWriter, r *http.Request) {
respondWithJSON(w, http.StatusOK, map[string]string{"data": "Game updated successfully"})
}

// fixme это общий респондер - надо его вынести отсюда
func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) {
w.Header().Set("Content-Type", "application/json")
response, err := json.Marshal(payload)
Expand Down
95 changes: 85 additions & 10 deletions src/lib/api/users.go
Original file line number Diff line number Diff line change
@@ -1,31 +1,106 @@
package api

import (
"crypto/sha1"
"ctf01d/lib/models"
"ctf01d/lib/repository"
"ctf01d/lib/view"
"database/sql"
"encoding/hex"
"encoding/json"
"net/http"

"github.com/gorilla/mux"
)

type RequestUser struct {
Username string `json:"user_name"`
Role string `json:"role"`
AvatarUrl string `json:"avatar_url"`
Status string `json:"status"`
Password string `json:"password"`
}

func CreateUserHandler(db *sql.DB, w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.WriteHeader(http.StatusOK)
var user RequestUser
if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
respondWithJSON(w, http.StatusBadRequest, map[string]string{"error": "Invalid request payload"})
return
}
userRepo := repository.NewUserRepository(db)
newUser := &models.User{
Username: user.Username,
Role: user.Role,
Status: user.Status,
PasswordHash: HashPassword(user.Password),
}
if err := userRepo.Create(r.Context(), newUser); err != nil {
respondWithJSON(w, http.StatusInternalServerError, map[string]string{"error": "Failed to create user: " + err.Error()})
return
}
respondWithJSON(w, http.StatusOK, map[string]string{"data": "User created successfully"})
}

// ей тут не место, вынести в - tool, добавить соль в конфиг и солить пароли
func HashPassword(s string) string {
h := sha1.New()
h.Write([]byte(s))
return hex.EncodeToString(h.Sum(nil))
}

func DeleteUserHandler(db *sql.DB, w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.WriteHeader(http.StatusOK)
vars := mux.Vars(r)
id := vars["id"]
userRepo := repository.NewUserRepository(db)
if err := userRepo.Delete(r.Context(), id); err != nil {
respondWithJSON(w, http.StatusInternalServerError, map[string]string{"error": "Failed to delete user"})
return
}
respondWithJSON(w, http.StatusOK, map[string]string{"data": "User deleted successfully"})
}

func GetUserByIdHandler(db *sql.DB, w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.WriteHeader(http.StatusOK)
vars := mux.Vars(r)
id := vars["id"]
userRepo := repository.NewUserRepository(db)
user, err := userRepo.GetById(r.Context(), id)
if err != nil {
respondWithJSON(w, http.StatusInternalServerError, map[string]string{"error": "Failed to fetch user"})
return
}
respondWithJSON(w, http.StatusOK, view.NewUserFromModel(user))
}

func ListUsersHandler(db *sql.DB, w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.WriteHeader(http.StatusOK)
userRepo := repository.NewUserRepository(db)
users, err := userRepo.List(r.Context())
if err != nil {
respondWithJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
return
}
respondWithJSON(w, http.StatusOK, view.NewUsersFromModels(users))
}

func UpdateUserHandler(db *sql.DB, w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.WriteHeader(http.StatusOK)
// fixme update не проверяет есть ли запись в бд
var user RequestUser
if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
respondWithJSON(w, http.StatusBadRequest, map[string]string{"error": "Invalid request payload"})
return
}
userRepo := repository.NewUserRepository(db)
updateUser := &models.User{
Username: user.Username,
Role: user.Role,
Status: user.Status,
}
vars := mux.Vars(r)
id := vars["id"]
updateUser.Id = id
err := userRepo.Update(r.Context(), updateUser)
if err != nil {
respondWithJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
}
respondWithJSON(w, http.StatusOK, map[string]string{"data": "User updated successfully"})
}
11 changes: 6 additions & 5 deletions src/lib/models/user.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package models

type User struct {
Id string `db:"id"`
Username string `db:"username"`
Role string `db:"role"`
AvatarUrl string `db:"avatar_url"`
Status string `db:"status"`
Id string `db:"id"`
Username string `db:"user_name"`
Role string `db:"role"`
AvatarUrl string `db:"avatar_url"`
Status string `db:"status"`
PasswordHash string `db:"password_hash"`
}
18 changes: 9 additions & 9 deletions src/lib/repository/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ type UserRepository interface {
GetById(ctx context.Context, id string) (*models.User, error)
Update(ctx context.Context, user *models.User) error
Delete(ctx context.Context, id string) error
List(ctx context.Context) ([]models.User, error)
List(ctx context.Context) ([]*models.User, error)
}

type userRepo struct {
Expand All @@ -23,13 +23,13 @@ func NewUserRepository(db *sql.DB) UserRepository {
}

func (r *userRepo) Create(ctx context.Context, user *models.User) error {
query := `INSERT INTO users (username, avatar_url, role) VALUES ($1, $2, $3)`
_, err := r.db.ExecContext(ctx, query, user.Username, user.AvatarUrl, user.Role)
query := `INSERT INTO users (user_name, avatar_url, role, status, password_hash) VALUES ($1, $2, $3, $4, $5)`
_, err := r.db.ExecContext(ctx, query, user.Username, user.AvatarUrl, user.Role, user.Status, user.PasswordHash)
return err
}

func (r *userRepo) GetById(ctx context.Context, id string) (*models.User, error) {
query := `SELECT id, username, avatar_url, role FROM users WHERE id = $1`
query := `SELECT id, user_name, avatar_url, role FROM users WHERE id = $1`
user := &models.User{}
err := r.db.QueryRowContext(ctx, query, id).Scan(&user.Id, &user.Username, &user.AvatarUrl, &user.Role)
if err != nil {
Expand All @@ -39,7 +39,7 @@ func (r *userRepo) GetById(ctx context.Context, id string) (*models.User, error)
}

func (r *userRepo) Update(ctx context.Context, user *models.User) error {
query := `UPDATE users SET username = $1, avatar_url = $2, role = $3 WHERE id = $4`
query := `UPDATE users SET user_name = $1, avatar_url = $2, role = $3 WHERE id = $4`
_, err := r.db.ExecContext(ctx, query, user.Username, user.AvatarUrl, user.Role, user.Id)
return err
}
Expand All @@ -50,21 +50,21 @@ func (r *userRepo) Delete(ctx context.Context, id string) error {
return err
}

func (r *userRepo) List(ctx context.Context) ([]models.User, error) {
query := `SELECT id, username, avatar_url, role FROM users`
func (r *userRepo) List(ctx context.Context) ([]*models.User, error) {
query := `SELECT id, user_name, avatar_url, role FROM users`
rows, err := r.db.QueryContext(ctx, query)
if err != nil {
return nil, err
}
defer rows.Close()

var users []models.User
var users []*models.User
for rows.Next() {
var user models.User
if err := rows.Scan(&user.Id, &user.Username, &user.AvatarUrl, &user.Role); err != nil {
return nil, err
}
users = append(users, user)
users = append(users, &user)
}
return users, nil
}
22 changes: 21 additions & 1 deletion src/lib/view/user.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,29 @@
package view

import "ctf01d/lib/models"

type User struct {
Id string `json:"id,omitempty"`
Username string `json:"username,omitempty"`
Username string `json:"user_name,omitempty"`
Role string `json:"role,omitempty"`
AvatarUrl string `json:"avatar_url,omitempty"`
Status string `json:"status,omitempty"`
}

func NewUserFromModel(u *models.User) *User {
return &User{
Id: u.Id,
Username: u.Username,
Role: u.Role,
AvatarUrl: u.AvatarUrl,
Status: u.Status,
}
}

func NewUsersFromModels(ms []*models.User) []*User {
var users []*User
for _, m := range ms {
users = append(users, NewUserFromModel(m))
}
return users
}

0 comments on commit 1378419

Please sign in to comment.