custom errors

This commit is contained in:
BENEDEK László 2025-06-04 19:11:54 +02:00
parent f52ad5f03a
commit 4854d926d7
21 changed files with 251 additions and 113 deletions

View File

@ -40,21 +40,14 @@ func register(c *gin.Context) {
} }
authController, err := controller.MakeAuthController() authController, err := controller.MakeAuthController()
if err != nil { if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{ sendError(c, err)
"error": err.Error(),
})
return return
} }
err = authController.Register(transaction.Username, transaction.Password, transaction.RepeatPassword) err = authController.Register(transaction.Username, transaction.Password, transaction.RepeatPassword)
// TODO: handle server errors/register violations separetly
if err != nil { if err != nil {
c.JSON(http.StatusOK, gin.H{ sendError(c, err)
"error": err.Error(),
})
return return
} }
@ -78,24 +71,13 @@ func login(c *gin.Context) {
authController, err := controller.MakeAuthController() authController, err := controller.MakeAuthController()
if err != nil { if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{ sendError(c, err)
"error": err.Error(),
})
return return
} }
token, ok, err := authController.Login(transaction.Username, transaction.Password) token, err := authController.Login(transaction.Username, transaction.Password)
if err != nil { if err != nil {
c.JSON(http.StatusOK, gin.H{ sendError(c, err)
"error": err.Error(),
})
return
}
if !ok {
c.JSON(http.StatusOK, gin.H{
"error": "bad credentials",
})
return return
} }
@ -109,18 +91,14 @@ func login(c *gin.Context) {
func logout(c *gin.Context) { func logout(c *gin.Context) {
authController, err := controller.MakeAuthController() authController, err := controller.MakeAuthController()
if err != nil { if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{ sendError(c, err)
"error": err.Error(),
})
return return
} }
token, _ := c.Get(SESSION_COOKIE) // must exist after isLoggedIn token, _ := c.Get(SESSION_COOKIE) // must exist after isLoggedIn
err = authController.Logout(token.(string)) err = authController.Logout(token.(string))
if err != nil { if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{ sendError(c, err)
"error": err.Error(),
})
return return
} }
@ -133,17 +111,13 @@ func logout(c *gin.Context) {
func bump(c *gin.Context) { func bump(c *gin.Context) {
authController, err := controller.MakeAuthController() authController, err := controller.MakeAuthController()
if err != nil { if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{ sendError(c, err)
"error": err.Error(),
})
return return
} }
token, _ := c.Get(SESSION_COOKIE) token, _ := c.Get(SESSION_COOKIE)
if err = authController.Bump(token.(string)); err != nil { if err = authController.Bump(token.(string)); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{ sendError(c, err)
"error": err.Error(),
})
return return
} }

View File

@ -18,6 +18,10 @@ func Listen(address string, base string) error {
auth.GET("logout", isLoggedIn, logout) auth.GET("logout", isLoggedIn, logout)
auth.GET("bump", isLoggedIn, bump) auth.GET("bump", isLoggedIn, bump)
user := api.Group("user")
user.Use(isLoggedIn)
user.GET("info/:username", userInfo)
server := &http.Server{ server := &http.Server{
Addr: address, Addr: address,
Handler: router, Handler: router,

12
api/errors.go Normal file
View File

@ -0,0 +1,12 @@
package api
import (
"git.tek.govt.hu/dowerx/chat/server/util"
"github.com/gin-gonic/gin"
)
func sendError(c *gin.Context, err *util.ChatError) {
c.JSON(err.Status(), gin.H{
"error": err.ErrorFromCode(),
})
}

31
api/user.go Normal file
View File

@ -0,0 +1,31 @@
package api
import (
"net/http"
"git.tek.govt.hu/dowerx/chat/server/controller"
"github.com/gin-gonic/gin"
)
const USERNAME_PARAM string = "username"
func userInfo(c *gin.Context) {
username := c.Param(USERNAME_PARAM)
userController, err := controller.MakeUserController()
if err != nil {
sendError(c, err)
return
}
user, err := userController.GetUser(username)
if err != nil {
sendError(c, err)
return
}
c.JSON(http.StatusOK, gin.H{
"message": "user found",
"user": user,
})
}

View File

@ -3,11 +3,11 @@ package controller
import ( import (
"crypto/rand" "crypto/rand"
"encoding/base64" "encoding/base64"
"errors"
"git.tek.govt.hu/dowerx/chat/server/config" "git.tek.govt.hu/dowerx/chat/server/config"
"git.tek.govt.hu/dowerx/chat/server/dao" "git.tek.govt.hu/dowerx/chat/server/dao"
"git.tek.govt.hu/dowerx/chat/server/model" "git.tek.govt.hu/dowerx/chat/server/model"
"git.tek.govt.hu/dowerx/chat/server/util"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
) )
@ -23,7 +23,7 @@ const (
TOKEN_LENGTH int = 32 TOKEN_LENGTH int = 32
) )
func (c *AuthController) init() error { func (c *AuthController) init() *util.ChatError {
userDAO, err := dao.MakeUserDAO() userDAO, err := dao.MakeUserDAO()
c.userDAO = userDAO c.userDAO = userDAO
if err != nil { if err != nil {
@ -39,22 +39,23 @@ func (c *AuthController) init() error {
return nil return nil
} }
func (c AuthController) Register(username string, password string, repeatPassword string) error { func (c AuthController) Register(username string, password string, repeatPassword string) *util.ChatError {
if len(username) < MIN_USERNAME_LENGTH { if len(username) < MIN_USERNAME_LENGTH {
return errors.New("username too short") return &util.ChatError{Message: "", Code: util.USERNAME_TOO_SHORT}
} }
if len(password) < MIN_PASSWORD_LENGTH { if len(password) < MIN_PASSWORD_LENGTH {
return errors.New("password too short") return &util.ChatError{Message: "", Code: util.PASSWORD_TOO_SHORT}
} }
if password != repeatPassword { if password != repeatPassword {
return errors.New("passwords don't match") return &util.ChatError{Message: "", Code: util.PASSWORDS_DONT_MATCH}
} }
hash, err := bcrypt.GenerateFromPassword([]byte(password), HASH_COST) hash, err := bcrypt.GenerateFromPassword([]byte(password), HASH_COST)
if err != nil { if err != nil {
return err return util.MakeError(err, util.GENERAL_ERROR)
} }
return c.userDAO.Create(model.User{ return c.userDAO.Create(model.User{
@ -73,38 +74,38 @@ func generateToken(length int) (string, error) {
return base64.URLEncoding.EncodeToString(b), nil return base64.URLEncoding.EncodeToString(b), nil
} }
func (c AuthController) Login(username string, password string) (string, bool, error) { func (c AuthController) Login(username string, password string) (string, *util.ChatError) {
user, err := c.userDAO.Read(model.User{Username: username}) user, err := c.userDAO.Read(model.User{Username: username})
if err != nil { if err != nil {
return "", false, err return "", err
} }
if err := bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(password)); err != nil { if err := bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(password)); err != nil {
return "", false, errors.New("wrong password") return "", &util.ChatError{Message: "", Code: util.WRONG_PASSWORD}
} }
token, err := generateToken(TOKEN_LENGTH) token, tokenErr := generateToken(TOKEN_LENGTH)
if err != nil { if tokenErr != nil {
return "", false, err return "", util.MakeError(err, util.GENERAL_ERROR)
} }
err = c.sessionDAO.DeleteAllByID(user.ID) err = c.sessionDAO.DeleteAllByID(user.ID)
if err != nil { if err != nil {
return "", false, err return "", err
} }
err = c.sessionDAO.Set(token, user.ID) err = c.sessionDAO.Set(token, user.ID)
if err != nil { if err != nil {
return "", false, err return "", err
} }
return token, true, nil return token, nil
} }
func (c AuthController) Logout(token string) error { func (c AuthController) Logout(token string) *util.ChatError {
return c.sessionDAO.Delete(token) return c.sessionDAO.Delete(token)
} }
func (c AuthController) Bump(token string) error { func (c AuthController) Bump(token string) *util.ChatError {
return c.sessionDAO.Bump(token, config.GetConfig().API.TokenLife) return c.sessionDAO.Bump(token, config.GetConfig().API.TokenLife)
} }

View File

@ -1,7 +1,15 @@
package controller package controller
func MakeAuthController() (AuthController, error) { import "git.tek.govt.hu/dowerx/chat/server/util"
func MakeAuthController() (AuthController, *util.ChatError) {
authController := &AuthController{} authController := &AuthController{}
err := authController.init() err := authController.init()
return *authController, err return *authController, err
} }
func MakeUserController() (UserController, *util.ChatError) {
userController := &UserController{}
err := userController.init()
return *userController, err
}

View File

@ -1,5 +1,7 @@
package controller package controller
import "git.tek.govt.hu/dowerx/chat/server/util"
type IController interface { type IController interface {
init() error init() *util.ChatError
} }

View File

@ -0,0 +1,25 @@
package controller
import (
"git.tek.govt.hu/dowerx/chat/server/dao"
"git.tek.govt.hu/dowerx/chat/server/model"
"git.tek.govt.hu/dowerx/chat/server/util"
)
type UserController struct {
userDAO dao.IUserDAO
}
func (c *UserController) init() *util.ChatError {
userDAO, err := dao.MakeUserDAO()
c.userDAO = userDAO
if err != nil {
return err
}
return nil
}
func (c UserController) GetUser(username string) (model.User, *util.ChatError) {
return c.userDAO.Read(model.User{Username: username})
}

View File

@ -3,15 +3,16 @@ package dao
import ( import (
"git.tek.govt.hu/dowerx/chat/server/dao/postgres" "git.tek.govt.hu/dowerx/chat/server/dao/postgres"
"git.tek.govt.hu/dowerx/chat/server/dao/valkey" "git.tek.govt.hu/dowerx/chat/server/dao/valkey"
"git.tek.govt.hu/dowerx/chat/server/util"
) )
func MakeUserDAO() (IUserDAO, error) { func MakeUserDAO() (IUserDAO, *util.ChatError) {
dao := &postgres.UserDAOPG{} dao := &postgres.UserDAOPG{}
err := dao.Init() err := dao.Init()
return dao, err return dao, err
} }
func MakeSessionDAO() (ISessionDAO, error) { func MakeSessionDAO() (ISessionDAO, *util.ChatError) {
dao := &valkey.SessionDAOVK{} dao := &valkey.SessionDAOVK{}
err := dao.Init() err := dao.Init()
return dao, err return dao, err

View File

@ -1,5 +1,7 @@
package dao package dao
import "git.tek.govt.hu/dowerx/chat/server/util"
type IDAO interface { type IDAO interface {
Init() error Init() *util.ChatError
} }

View File

@ -1,9 +1,11 @@
package dao package dao
import "git.tek.govt.hu/dowerx/chat/server/util"
type ISessionDAO interface { type ISessionDAO interface {
Set(token string, id int) error Set(token string, id int) *util.ChatError
Get(token string) (int, error) Get(token string) (int, *util.ChatError)
Delete(token string) error Delete(token string) *util.ChatError
DeleteAllByID(id int) error DeleteAllByID(id int) *util.ChatError
Bump(token string, time int) error Bump(token string, time int) *util.ChatError
} }

View File

@ -1,11 +1,14 @@
package dao package dao
import "git.tek.govt.hu/dowerx/chat/server/model" import (
"git.tek.govt.hu/dowerx/chat/server/model"
"git.tek.govt.hu/dowerx/chat/server/util"
)
type IUserDAO interface { type IUserDAO interface {
Create(user model.User) error Create(user model.User) *util.ChatError
Read(user model.User) (model.User, error) Read(user model.User) (model.User, *util.ChatError)
List() ([]model.User, error) List() ([]model.User, *util.ChatError)
Update(user model.User) error Update(user model.User) *util.ChatError
Delete(user model.User) error Delete(user model.User) *util.ChatError
} }

View File

@ -4,18 +4,19 @@ import (
"fmt" "fmt"
"git.tek.govt.hu/dowerx/chat/server/config" "git.tek.govt.hu/dowerx/chat/server/config"
"git.tek.govt.hu/dowerx/chat/server/util"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
_ "github.com/lib/pq" _ "github.com/lib/pq"
) )
var db *sqlx.DB var db *sqlx.DB
func getDatabase() (*sqlx.DB, error) { func getDatabase() (*sqlx.DB, *util.ChatError) {
if db == nil { if db == nil {
cfg := config.GetConfig() cfg := config.GetConfig()
newDB, err := sqlx.Connect("postgres", fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", cfg.Database.Host, cfg.Database.Port, cfg.Database.User, cfg.Database.Password, cfg.Database.DBname)) newDB, err := sqlx.Connect("postgres", fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", cfg.Database.Host, cfg.Database.Port, cfg.Database.User, cfg.Database.Password, cfg.Database.DBname))
if err != nil { if err != nil {
return nil, err return nil, util.MakeError(err, util.DATABASE_CONNECTION_FAULT)
} }
db = newDB db = newDB
} }

View File

@ -1,9 +1,8 @@
package postgres package postgres
import ( import (
"errors"
"git.tek.govt.hu/dowerx/chat/server/model" "git.tek.govt.hu/dowerx/chat/server/model"
"git.tek.govt.hu/dowerx/chat/server/util"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
) )
@ -12,13 +11,13 @@ type UserDAOPG struct {
} }
// Create a new user // Create a new user
func (d UserDAOPG) Create(user model.User) error { func (d UserDAOPG) Create(user model.User) *util.ChatError {
_, err := d.db.NamedExec(`call add_user(:username, :password_hash)`, &user) _, err := d.db.NamedExec(`call add_user(:username, :password_hash)`, &user)
return err return util.MakeError(err, util.DATABASE_QUERY_FAULT)
} }
// Read returns a user by ID if ID != 0, else by Username // Read returns a user by ID if ID != 0, else by Username
func (d UserDAOPG) Read(user model.User) (model.User, error) { func (d UserDAOPG) Read(user model.User) (model.User, *util.ChatError) {
var rows *sqlx.Rows var rows *sqlx.Rows
var err error var err error
if user.ID != 0 { if user.ID != 0 {
@ -28,22 +27,22 @@ func (d UserDAOPG) Read(user model.User) (model.User, error) {
} }
if err != nil { if err != nil {
return user, err return user, util.MakeError(err, util.DATABASE_QUERY_FAULT)
} }
if !rows.Next() { if !rows.Next() {
return user, errors.New("no such user") return user, util.MakeError(err, util.USER_NOT_FOUND)
} }
err = rows.StructScan(&user) err = rows.StructScan(&user)
return user, err return user, util.MakeError(err, util.DATABASE_QUERY_FAULT)
} }
// List all users // List all users
func (d UserDAOPG) List() ([]model.User, error) { func (d UserDAOPG) List() ([]model.User, *util.ChatError) {
rows, err := d.db.Queryx(`select * from "user" order by "username"`) rows, err := d.db.Queryx(`select * from "user" order by "username"`)
if err != nil { if err != nil {
return nil, err return nil, util.MakeError(err, util.DATABASE_QUERY_FAULT)
} }
users := make([]model.User, 0) users := make([]model.User, 0)
@ -59,17 +58,17 @@ func (d UserDAOPG) List() ([]model.User, error) {
users = append(users, user) users = append(users, user)
} }
return users, err return users, util.MakeError(err, util.DATABASE_QUERY_FAULT)
} }
// Update sets all the fields of the User with the given ID // Update sets all the fields of the User with the given ID
func (d UserDAOPG) Update(user model.User) error { func (d UserDAOPG) Update(user model.User) *util.ChatError {
_, err := d.db.NamedExec(`update "user" set "username" = :username, "password_hash" = :password_hash, "status" = :status, "picture" = :picture, "bio" = :bio where "id" = :id`, &user) _, err := d.db.NamedExec(`update "user" set "username" = :username, "password_hash" = :password_hash, "status" = :status, "picture" = :picture, "bio" = :bio where "id" = :id`, &user)
return err return util.MakeError(err, util.DATABASE_QUERY_FAULT)
} }
// Delete removes a user by ID if ID != 0, else by Username // Delete removes a user by ID if ID != 0, else by Username
func (d UserDAOPG) Delete(user model.User) error { func (d UserDAOPG) Delete(user model.User) *util.ChatError {
var err error var err error
if user.ID != 0 { if user.ID != 0 {
_, err = d.db.NamedExec(`delete from "user" where "id" = :id`, &user) _, err = d.db.NamedExec(`delete from "user" where "id" = :id`, &user)
@ -77,5 +76,5 @@ func (d UserDAOPG) Delete(user model.User) error {
_, err = d.db.NamedExec(`delete from "user" where "username" = :username`, &user) _, err = d.db.NamedExec(`delete from "user" where "username" = :username`, &user)
} }
return err return util.MakeError(err, util.DATABASE_QUERY_FAULT)
} }

View File

@ -1,6 +1,7 @@
package postgres package postgres
import ( import (
"git.tek.govt.hu/dowerx/chat/server/util"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
) )
@ -8,7 +9,7 @@ type pgDAO struct {
db *sqlx.DB db *sqlx.DB
} }
func (d *pgDAO) Init() error { func (d *pgDAO) Init() *util.ChatError {
conn, err := getDatabase() conn, err := getDatabase()
d.db = conn d.db = conn
return err return err

View File

@ -2,16 +2,17 @@ package valkey
import ( import (
"git.tek.govt.hu/dowerx/chat/server/config" "git.tek.govt.hu/dowerx/chat/server/config"
"git.tek.govt.hu/dowerx/chat/server/util"
"github.com/valkey-io/valkey-go" "github.com/valkey-io/valkey-go"
) )
var vk valkey.Client = nil var vk valkey.Client = nil
func getClient() (*valkey.Client, error) { func getClient() (*valkey.Client, *util.ChatError) {
if vk == nil { if vk == nil {
client, err := valkey.NewClient(config.GetConfig().Valkey) client, err := valkey.NewClient(config.GetConfig().Valkey)
vk = client vk = client
return &vk, err return &vk, util.MakeError(err, util.DATABASE_CONNECTION_FAULT)
} }
return &vk, nil return &vk, nil

View File

@ -5,6 +5,7 @@ import (
"strconv" "strconv"
"git.tek.govt.hu/dowerx/chat/server/config" "git.tek.govt.hu/dowerx/chat/server/config"
"git.tek.govt.hu/dowerx/chat/server/util"
) )
const SESSION_PREFIX string = "session:" const SESSION_PREFIX string = "session:"
@ -13,28 +14,28 @@ type SessionDAOVK struct {
vkDAO vkDAO
} }
func (d SessionDAOVK) Set(token string, id int) error { func (d SessionDAOVK) Set(token string, id int) *util.ChatError {
cmd := (*d.vk).B().Set().Key(SESSION_PREFIX + token).Value(strconv.Itoa(id)).ExSeconds(int64(config.GetConfig().API.TokenLife)).Build() cmd := (*d.vk).B().Set().Key(SESSION_PREFIX + token).Value(strconv.Itoa(id)).ExSeconds(int64(config.GetConfig().API.TokenLife)).Build()
return (*d.vk).Do(context.Background(), cmd).Error() return util.MakeError((*d.vk).Do(context.Background(), cmd).Error(), util.DATABASE_QUERY_FAULT)
} }
func (d SessionDAOVK) Get(token string) (int, error) { func (d SessionDAOVK) Get(token string) (int, *util.ChatError) {
cmd := (*d.vk).B().Get().Key(SESSION_PREFIX + token).Build() cmd := (*d.vk).B().Get().Key(SESSION_PREFIX + token).Build()
result := (*d.vk).Do(context.Background(), cmd) result := (*d.vk).Do(context.Background(), cmd)
if err := result.Error(); err != nil { if err := result.Error(); err != nil {
return 0, err return 0, util.MakeError(err, util.DATABASE_QUERY_FAULT)
} }
id, err := result.AsInt64() id, err := result.AsInt64()
return int(id), err return int(id), util.MakeError(err, util.DATABASE_QUERY_FAULT)
} }
func (d SessionDAOVK) Delete(token string) error { func (d SessionDAOVK) Delete(token string) *util.ChatError {
cmd := (*d.vk).B().Del().Key(SESSION_PREFIX + token).Build() cmd := (*d.vk).B().Del().Key(SESSION_PREFIX + token).Build()
return (*d.vk).Do(context.Background(), cmd).Error() return util.MakeError((*d.vk).Do(context.Background(), cmd).Error(), util.DATABASE_QUERY_FAULT)
} }
func (d SessionDAOVK) DeleteAllByID(id int) error { func (d SessionDAOVK) DeleteAllByID(id int) *util.ChatError {
// iterate all session keys // iterate all session keys
var cursor uint64 = 0 var cursor uint64 = 0
pattern := SESSION_PREFIX + "*" pattern := SESSION_PREFIX + "*"
@ -43,12 +44,12 @@ func (d SessionDAOVK) DeleteAllByID(id int) error {
result := (*d.vk).Do(context.Background(), cmd) result := (*d.vk).Do(context.Background(), cmd)
if err := result.Error(); err != nil { if err := result.Error(); err != nil {
return err return util.MakeError(err, util.DATABASE_QUERY_FAULT)
} }
entry, err := result.AsScanEntry() entry, err := result.AsScanEntry()
if err != nil { if err != nil {
return err return util.MakeError(err, util.DATABASE_QUERY_FAULT)
} }
for _, key := range entry.Elements { for _, key := range entry.Elements {
@ -57,12 +58,12 @@ func (d SessionDAOVK) DeleteAllByID(id int) error {
result := (*d.vk).Do(context.Background(), cmd) result := (*d.vk).Do(context.Background(), cmd)
if err := result.Error(); err != nil { if err := result.Error(); err != nil {
return err return util.MakeError(err, util.DATABASE_QUERY_FAULT)
} }
value, err := result.AsInt64() value, err := result.AsInt64()
if err != nil { if err != nil {
return err return util.MakeError(err, util.DATABASE_QUERY_FAULT)
} }
// check if the value is the same as our id // check if the value is the same as our id
@ -71,7 +72,7 @@ func (d SessionDAOVK) DeleteAllByID(id int) error {
cmd = (*d.vk).B().Del().Key(key).Build() cmd = (*d.vk).B().Del().Key(key).Build()
result := (*d.vk).Do(context.Background(), cmd) result := (*d.vk).Do(context.Background(), cmd)
if err := result.Error(); err != nil { if err := result.Error(); err != nil {
return err return util.MakeError(err, util.DATABASE_QUERY_FAULT)
} }
} }
} }
@ -83,7 +84,7 @@ func (d SessionDAOVK) DeleteAllByID(id int) error {
return nil return nil
} }
func (d SessionDAOVK) Bump(token string, time int) error { func (d SessionDAOVK) Bump(token string, time int) *util.ChatError {
cmd := (*d.vk).B().Expire().Key(SESSION_PREFIX + token).Seconds(int64(time)).Build() cmd := (*d.vk).B().Expire().Key(SESSION_PREFIX + token).Seconds(int64(time)).Build()
return (*d.vk).Do(context.Background(), cmd).Error() return util.MakeError((*d.vk).Do(context.Background(), cmd).Error(), util.DATABASE_QUERY_FAULT)
} }

View File

@ -1,6 +1,7 @@
package valkey package valkey
import ( import (
"git.tek.govt.hu/dowerx/chat/server/util"
"github.com/valkey-io/valkey-go" "github.com/valkey-io/valkey-go"
) )
@ -8,7 +9,7 @@ type vkDAO struct {
vk *valkey.Client vk *valkey.Client
} }
func (d *vkDAO) Init() error { func (d *vkDAO) Init() *util.ChatError {
client, err := getClient() client, err := getClient()
d.vk = client d.vk = client
return err return err

View File

@ -4,8 +4,8 @@ import "time"
type Message struct { type Message struct {
ID int ID int
Sender User Sender string // username
Channel Channel Channel Channel // channel.id
Time time.Time Time time.Time
Content string Content string
} }

View File

@ -1,10 +1,10 @@
package model package model
type User struct { type User struct {
ID int `db:"id"` ID int `db:"id" json:"-"`
Username string `db:"username"` Username string `db:"username" json:"username"`
PasswordHash string `db:"password_hash"` PasswordHash string `db:"password_hash" json:"-"`
Status string `db:"status"` Status string `db:"status" json:"status"`
Picture string `db:"picture"` Picture string `db:"picture" json:"picture"`
Bio string `db:"bio"` Bio string `db:"bio" json:"bio"`
} }

69
util/errors.go Normal file
View File

@ -0,0 +1,69 @@
package util
import "net/http"
type ChatErrorCode int
// List off all known error codes
const (
// internalServerError
GENERAL_ERROR ChatErrorCode = iota
DATABASE_CONNECTION_FAULT
DATABASE_QUERY_FAULT
// statusOk
USER_NOT_FOUND
WRONG_PASSWORD
USERNAME_TOO_SHORT
PASSWORD_TOO_SHORT
PASSWORDS_DONT_MATCH
)
var codeToMessage = map[ChatErrorCode]string{}
type ChatError struct {
Message string
Code ChatErrorCode
}
// Error returns the original errors message if not empty, else returns ErrorFromCode()
func (e ChatError) Error() string {
if e.Message != "" {
return e.Message
} else {
return e.ErrorFromCode()
}
}
// ErrorFromCode returns a string that is safe to show in API responses
func (e *ChatError) ErrorFromCode() string {
message, ok := codeToMessage[e.Code]
if ok {
return message
} else {
return "unknown error code"
}
}
// Status returns the http status of the error type
func (e *ChatError) Status() int {
switch e.Code {
case USER_NOT_FOUND:
case WRONG_PASSWORD:
case USERNAME_TOO_SHORT:
case PASSWORD_TOO_SHORT:
case PASSWORDS_DONT_MATCH:
return http.StatusOK
default:
return http.StatusInternalServerError
}
return http.StatusInternalServerError
}
// MakeError makes an error with the given code id err exists
func MakeError(err error, code ChatErrorCode) *ChatError {
if err == nil {
return nil
}
return &ChatError{Message: err.Error(), Code: code}
}