Compare commits
2 Commits
f52ad5f03a
...
de83676794
Author | SHA1 | Date | |
---|---|---|---|
de83676794 | |||
4854d926d7 |
44
api/auth.go
44
api/auth.go
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
12
api/errors.go
Normal 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
31
api/user.go
Normal 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,
|
||||||
|
})
|
||||||
|
}
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
25
controller/UserController.go
Normal file
25
controller/UserController.go
Normal 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})
|
||||||
|
}
|
@ -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
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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.ChatError{Message: "", Code: 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)
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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"`
|
||||||
}
|
}
|
||||||
|
83
util/errors.go
Normal file
83
util/errors.go
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
package util
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
type ChatErrorCode int
|
||||||
|
|
||||||
|
// List off all known error codes
|
||||||
|
|
||||||
|
const (
|
||||||
|
// GENERAL_ERROR for not classified system errros
|
||||||
|
GENERAL_ERROR ChatErrorCode = iota
|
||||||
|
|
||||||
|
DATABASE_CONNECTION_FAULT
|
||||||
|
DATABASE_QUERY_FAULT
|
||||||
|
|
||||||
|
USER_NOT_FOUND
|
||||||
|
WRONG_PASSWORD
|
||||||
|
USERNAME_TOO_SHORT
|
||||||
|
PASSWORD_TOO_SHORT
|
||||||
|
PASSWORDS_DONT_MATCH
|
||||||
|
)
|
||||||
|
|
||||||
|
var codeToMessage = map[ChatErrorCode]string{
|
||||||
|
GENERAL_ERROR: "an unexpected error occurred",
|
||||||
|
DATABASE_CONNECTION_FAULT: "database connection failed",
|
||||||
|
DATABASE_QUERY_FAULT: "database query failed",
|
||||||
|
USER_NOT_FOUND: "user not found",
|
||||||
|
WRONG_PASSWORD: "incorrect password",
|
||||||
|
USERNAME_TOO_SHORT: "username is too short",
|
||||||
|
PASSWORD_TOO_SHORT: "password is too short",
|
||||||
|
PASSWORDS_DONT_MATCH: "passwords do not match",
|
||||||
|
}
|
||||||
|
|
||||||
|
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:
|
||||||
|
fallthrough
|
||||||
|
case WRONG_PASSWORD:
|
||||||
|
fallthrough
|
||||||
|
case USERNAME_TOO_SHORT:
|
||||||
|
fallthrough
|
||||||
|
case PASSWORD_TOO_SHORT:
|
||||||
|
fallthrough
|
||||||
|
case PASSWORDS_DONT_MATCH:
|
||||||
|
return http.StatusOK
|
||||||
|
default:
|
||||||
|
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}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user