appidea-restapi/routers/api/v1/user/user.go

364 lines
7.8 KiB
Go

package user
import (
"crypto/rand"
"encoding/json"
"errors"
"math/big"
"regexp"
"strings"
"unicode"
"git.umbach.dev/app-idea/rest-api/modules/config"
"git.umbach.dev/app-idea/rest-api/modules/database"
"git.umbach.dev/app-idea/rest-api/modules/rabbitmq"
"git.umbach.dev/app-idea/rest-api/modules/structs"
"git.umbach.dev/app-idea/rest-api/modules/utils"
"github.com/gofiber/fiber/v2"
log "github.com/sirupsen/logrus"
"gorm.io/gorm"
)
var cfg = &config.Cfg
func generateRandomString(n int, t int) (string, error) {
var letters string
if t == 1 {
letters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
} else {
letters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
}
r := make([]byte, n)
for i := 0; i < n; i++ {
num, err := rand.Int(rand.Reader, big.NewInt(int64(len(letters))))
if err != nil {
return "", err
}
r[i] = letters[num.Int64()]
}
return string(r), nil
}
func generateRandomHashtag(db *gorm.DB, n int) (string, error) {
c := make(chan bool)
var s string
var err error
for {
s, err = generateRandomString(6, 0)
if err != nil {
log.Warnln("error generating hashtag:", err)
return "", err
}
go func() {
c <- isHashtagValid(db, 0, s)
}()
if msg := <-c; msg {
break
}
}
return s, nil
}
func isHashtagValid(db *gorm.DB, t int, h string) bool {
if t == 1 && !isUpper(h) || len(h) < 2 || len(h) > 6 {
return false
}
var res string
db.Raw("SELECT hashtag FROM users WHERE hashtag = ?", h).Scan(&res)
if res == "" {
return true
} else {
return false
}
}
func isUpper(s string) bool {
for _, r := range s {
if !unicode.IsUpper(r) && unicode.IsLetter(r) {
return false
}
}
return true
}
func isUsernameValid(u string) bool {
if len(u) < int(cfg.Settings.Lengths.UsernameMinLen) || len(u) > int(cfg.Settings.Lengths.UsernameMaxLen) {
return false
}
return true
}
var emailRegex = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")
func isEmailValid(e string) bool {
if len(e) < int(cfg.Settings.Lengths.EmailMinLen) || len(e) > int(cfg.Settings.Lengths.EmailMaxLen) {
return false
}
return emailRegex.MatchString(e)
}
func isPasswordValid(p string) bool {
if len(p) < int(cfg.Settings.Lengths.PasswordMinLen) || len(p) > int(cfg.Settings.Lengths.PasswordMaxLen) {
return false
}
return true
}
func isEmailAvailable(db *gorm.DB, email string) bool {
var res string
db.Raw("SELECT email FROM users WHERE email = ?", email).Scan(&res)
if res == "" {
return true
} else {
return false
}
}
func getUserIdBySessionId(sessionId string) (string, error) {
db := database.DB
session := structs.Session{}
db.Select("user_id").Where(cfg.Settings.Cookies.SessionId+" = ?", sessionId).Find(&session)
return session.UserId, nil
}
func GetUserById(c *fiber.Ctx) error {
// swagger:operation GET /users User user
// ---
// summary: Informations about an user by id (except password)
// parameters:
// - name: v
// in: query
// description: Example -> { "v"; ["name", "state", "language_id"] }
// type: string
// required: true
// responses:
// '200':
// description: User informations
// '400':
// description: Values wrong format
return userInfos(c, c.Params("id"))
}
func GetUser(c *fiber.Ctx) error {
// swagger:operation GET /user User user
// ---
// summary: Informations about the user (except password)
// parameters:
// - name: v
// in: query
// description: Example -> { "v"; ["name", "state", "language_id"] }
// type: string
// required: true
// responses:
// '200':
// description: User informations
// '400':
// description: Values wrong format
return userInfos(c, "")
}
func userInfos(c *fiber.Ctx, userId string) error {
type Input struct {
Values []string `json:"v"`
}
var input Input
if err := c.BodyParser(&input); err != nil {
return c.SendStatus(fiber.StatusBadRequest)
}
value := strings.Join(input.Values, ",")
if strings.Contains(value, "password") {
return c.SendStatus(fiber.StatusForbidden)
}
db := database.DB
if userId == "" {
var err error
userId, err = getUserIdBySessionId(c.Cookies(cfg.Settings.Cookies.SessionId))
if err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
}
user := map[string]interface{}{}
res := db.Model(&structs.User{}).Select(value).First(&user, "id = ?", userId)
if res.Error != nil {
return c.SendStatus(fiber.StatusBadRequest)
}
return c.JSON(&user)
}
func GetUserBySessionId(sessionId string) (structs.User, error) {
db := database.DB
user := structs.User{}
userId, err := getUserIdBySessionId(sessionId)
if err != nil {
return user, err
}
err = db.Where("id = ?", userId).First(&user).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
return user, err
}
return user, nil
}
func deleteUser(c *fiber.Ctx) error {
userAction := structs.UserAction{Id: c.Params("actionId")}
db := database.DB
db.First(&userAction)
db.Delete(&structs.User{Id: userAction.UserId})
db.Where("user_id = ?", userAction.UserId).Delete(&structs.Session{})
db.Delete(&userAction)
return c.SendStatus(fiber.StatusOK)
}
func DeleteUser(c *fiber.Ctx) error {
user, err := GetUserBySessionId(c.Cookies(cfg.Settings.Cookies.SessionId))
if err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
userActivationId, err := createUserAction(user.Id)
if err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
user.State = 2
db := database.DB
db.Save(&user)
rabbitmq.PublishMail(structs.RabbitmqMailMessage{Mail: user.Email, TemplateId: 1, LanguageId: user.LanguageId, BodyData: json.RawMessage(`{"name": "` + user.Name + `",
"email": "` + user.Email + `",
"url": "http://localhost:3000/api/v1/user/action/1/` + userActivationId + `"}`)})
return c.SendStatus(fiber.StatusOK)
}
func GetUsers(c *fiber.Ctx) error {
db := database.DB
log.Info("here")
users := []structs.APIUser{}
db.Model(&structs.User{}).Find(&users)
return c.JSON(users)
}
func UpdateUser(c *fiber.Ctx) error {
file, err := c.FormFile("image")
if err != nil {
log.Debugln("FormFile err", err)
return c.SendStatus(fiber.StatusInternalServerError)
}
bytes, err := utils.FormFileToBytes(file)
if err != nil {
log.Debugln("formFileToBytes err", err)
return c.SendStatus(fiber.StatusInternalServerError)
}
log.Info(len(bytes))
user, err := GetUserBySessionId(c.Cookies(config.Cfg.Settings.Cookies.SessionId))
if err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
// update user avatar
filename, err := UpdateAvatar(c.Cookies(config.Cfg.Settings.Cookies.SessionId), user, bytes, file.Filename)
if err != nil {
log.Debugln("savepic err", err)
return c.SendStatus(fiber.StatusInternalServerError)
}
return c.JSON(fiber.Map{"filename": filename})
}
func UpdateAvatar(sessionId string, user structs.User, bytes []byte, filename string) (string, error) {
newFilename := strings.Replace("avatar"+filename, "-", "", -1)
rabbitmq.PublishPicture(structs.RabbitmqPictureMessage{Picture: bytes, UserId: user.Id, Filename: newFilename})
db := database.DB
res := db.Model(&structs.User{}).Where("id = ?", user.Id).Update("avatar_url", newFilename)
// TODO: delete old avatar from storage server
if res.Error != nil {
log.Warnln("Failed to insert user to db:", res.Error)
return "", res.Error
}
return newFilename, nil
}
/*
func GetUsers(c *fiber.Ctx) error {
list := []string{}
/*
var (
name string
)*/
/*
rows, err := db.Query("SELECT username FROM users;")
fmt.Println("err", err)
defer rows.Close()
fmt.Println("reading data:")
for rows.Next() {
err := rows.Scan(&name)
fmt.Printf("Data row = (%s, %s)\n", name, err)
list = append(list, name)
}
err = rows.Err()*/
/*
return c.JSON(list)
}
*/