- Assign default circle to user when leaving a circle
- Support Pushover - Support Disable Signup - Migrate chatID to TargetID
This commit is contained in:
parent
850d472445
commit
adf5c0c0cd
20 changed files with 362 additions and 151 deletions
|
@ -14,6 +14,7 @@ import (
|
|||
cModel "donetick.com/core/internal/circle/model"
|
||||
cRepo "donetick.com/core/internal/circle/repo"
|
||||
"donetick.com/core/internal/email"
|
||||
nModel "donetick.com/core/internal/notifier/model"
|
||||
uModel "donetick.com/core/internal/user/model"
|
||||
uRepo "donetick.com/core/internal/user/repo"
|
||||
"donetick.com/core/internal/utils"
|
||||
|
@ -26,20 +27,22 @@ import (
|
|||
)
|
||||
|
||||
type Handler struct {
|
||||
userRepo *uRepo.UserRepository
|
||||
circleRepo *cRepo.CircleRepository
|
||||
jwtAuth *jwt.GinJWTMiddleware
|
||||
email *email.EmailSender
|
||||
isDonetickDotCom bool
|
||||
userRepo *uRepo.UserRepository
|
||||
circleRepo *cRepo.CircleRepository
|
||||
jwtAuth *jwt.GinJWTMiddleware
|
||||
email *email.EmailSender
|
||||
isDonetickDotCom bool
|
||||
IsUserCreationDisabled bool
|
||||
}
|
||||
|
||||
func NewHandler(ur *uRepo.UserRepository, cr *cRepo.CircleRepository, jwtAuth *jwt.GinJWTMiddleware, email *email.EmailSender, config *config.Config) *Handler {
|
||||
return &Handler{
|
||||
userRepo: ur,
|
||||
circleRepo: cr,
|
||||
jwtAuth: jwtAuth,
|
||||
email: email,
|
||||
isDonetickDotCom: config.IsDoneTickDotCom,
|
||||
userRepo: ur,
|
||||
circleRepo: cr,
|
||||
jwtAuth: jwtAuth,
|
||||
email: email,
|
||||
isDonetickDotCom: config.IsDoneTickDotCom,
|
||||
IsUserCreationDisabled: config.IsUserCreationDisabled,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -68,6 +71,12 @@ func (h *Handler) GetAllUsers() gin.HandlerFunc {
|
|||
}
|
||||
|
||||
func (h *Handler) signUp(c *gin.Context) {
|
||||
if h.IsUserCreationDisabled {
|
||||
c.JSON(403, gin.H{
|
||||
"error": "User creation is disabled",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
type SignUpReq struct {
|
||||
Username string `json:"username" binding:"required,min=4,max=20"`
|
||||
|
@ -497,8 +506,8 @@ func (h *Handler) UpdateNotificationTarget(c *gin.Context) {
|
|||
}
|
||||
|
||||
type Request struct {
|
||||
Type uModel.UserNotificationType `json:"type" binding:"required"`
|
||||
Token string `json:"token" binding:"required"`
|
||||
Type nModel.NotificationType `json:"type"`
|
||||
Target string `json:"target" binding:"required"`
|
||||
}
|
||||
|
||||
var req Request
|
||||
|
@ -506,13 +515,28 @@ func (h *Handler) UpdateNotificationTarget(c *gin.Context) {
|
|||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request"})
|
||||
return
|
||||
}
|
||||
if req.Type == nModel.NotificationTypeNone {
|
||||
err := h.userRepo.DeleteNotificationTarget(c, currentUser.ID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete notification target"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{})
|
||||
return
|
||||
}
|
||||
|
||||
err := h.userRepo.UpdateNotificationTarget(c, currentUser.ID, req.Token, req.Type)
|
||||
err := h.userRepo.UpdateNotificationTarget(c, currentUser.ID, req.Target, req.Type)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update notification target"})
|
||||
return
|
||||
}
|
||||
|
||||
err = h.userRepo.UpdateNotificationTargetForAllNotifications(c, currentUser.ID, req.Target, req.Type)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update notification target for all notifications"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{})
|
||||
}
|
||||
|
||||
|
@ -579,11 +603,12 @@ func Routes(router *gin.Engine, h *Handler, auth *jwt.GinJWTMiddleware, limiter
|
|||
authRoutes := router.Group("auth")
|
||||
authRoutes.Use(utils.RateLimitMiddleware(limiter))
|
||||
{
|
||||
authRoutes.POST("/:provider/callback", h.thirdPartyAuthCallback)
|
||||
authRoutes.POST("/", h.signUp)
|
||||
authRoutes.POST("login", auth.LoginHandler)
|
||||
authRoutes.GET("refresh", auth.RefreshHandler)
|
||||
authRoutes.POST("/:provider/callback", h.thirdPartyAuthCallback)
|
||||
authRoutes.POST("reset", h.resetPassword)
|
||||
authRoutes.POST("password", h.updateUserPassword)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
package user
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"time"
|
||||
|
||||
nModel "donetick.com/core/internal/notifier/model"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
ID int `json:"id" gorm:"primary_key"` // Unique identifier
|
||||
|
@ -16,10 +20,10 @@ type User struct {
|
|||
UpdatedAt time.Time `json:"updated_at" gorm:"column:updated_at"` // Updated at
|
||||
Disabled bool `json:"disabled" gorm:"column:disabled"` // Disabled
|
||||
// Email string `json:"email" gorm:"column:email"` // Email
|
||||
CustomerID *string `gorm:"column:customer_id;<-:false"` // read only column
|
||||
Subscription *string `json:"subscription" gorm:"column:subscription;<-:false"` // read only column
|
||||
Expiration *string `json:"expiration" gorm:"column:expiration;<-:false"` // read only column
|
||||
UserNotificationTargets []UserNotificationTarget `json:"-" gorm:"foreignKey:UserID;references:ID"`
|
||||
CustomerID *string `gorm:"column:customer_id;<-:false"` // read only column
|
||||
Subscription *string `json:"subscription" gorm:"column:subscription;<-:false"` // read only column
|
||||
Expiration *string `json:"expiration" gorm:"column:expiration;<-:false"` // read only column
|
||||
UserNotificationTargets UserNotificationTarget `json:"notification_target" gorm:"foreignKey:UserID;references:ID"`
|
||||
}
|
||||
|
||||
type UserPasswordReset struct {
|
||||
|
@ -39,18 +43,8 @@ type APIToken struct {
|
|||
}
|
||||
|
||||
type UserNotificationTarget struct {
|
||||
ID int `json:"id" gorm:"primary_key"` // Unique identifier
|
||||
UserID int `json:"userId" gorm:"column:user_id;index"` // Index on userID
|
||||
Type UserNotificationType `json:"type" gorm:"column:type"` // Type
|
||||
TargetID string `json:"targetId" gorm:"column:target_id"` // Target ID
|
||||
CreatedAt time.Time `json:"createdAt" gorm:"column:created_at"`
|
||||
UserID int `json:"userId" gorm:"column:user_id;index;primaryKey"` // Index on userID
|
||||
Type nModel.NotificationType `json:"type" gorm:"column:type"` // Type
|
||||
TargetID string `json:"target_id" gorm:"column:target_id"` // Target ID
|
||||
CreatedAt time.Time `json:"-" gorm:"column:created_at"`
|
||||
}
|
||||
|
||||
type UserNotificationType int8
|
||||
|
||||
const (
|
||||
_ UserNotificationType = iota
|
||||
Android
|
||||
IOS
|
||||
Telegram
|
||||
)
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"time"
|
||||
|
||||
"donetick.com/core/config"
|
||||
nModel "donetick.com/core/internal/notifier/model"
|
||||
uModel "donetick.com/core/internal/user/model"
|
||||
"donetick.com/core/logging"
|
||||
"gorm.io/gorm"
|
||||
|
@ -54,11 +55,11 @@ func (r *UserRepository) CreateUser(c context.Context, user *uModel.User) (*uMod
|
|||
func (r *UserRepository) GetUserByUsername(c context.Context, username string) (*uModel.User, error) {
|
||||
var user *uModel.User
|
||||
if r.isDonetickDotCom {
|
||||
if err := r.db.WithContext(c).Table("users u").Select("u.*, ss.status as subscription, ss.expired_at as expiration").Joins("left join stripe_customers sc on sc.user_id = u.id ").Joins("left join stripe_subscriptions ss on sc.customer_id = ss.customer_id").Where("username = ?", username).First(&user).Error; err != nil {
|
||||
if err := r.db.WithContext(c).Preload("UserNotificationTargets").Table("users u").Select("u.*, ss.status as subscription, ss.expired_at as expiration").Joins("left join stripe_customers sc on sc.user_id = u.id ").Joins("left join stripe_subscriptions ss on sc.customer_id = ss.customer_id").Where("username = ?", username).First(&user).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
if err := r.db.WithContext(c).Table("users u").Select("u.*, 'active' as subscription, '2999-12-31' as expiration").Where("username = ?", username).First(&user).Error; err != nil {
|
||||
if err := r.db.WithContext(c).Preload("UserNotificationTargets").Table("users u").Select("u.*, 'active' as subscription, '2999-12-31' as expiration").Where("username = ?", username).First(&user).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
@ -159,10 +160,22 @@ func (r *UserRepository) DeleteAPIToken(c context.Context, userID int, tokenID s
|
|||
return r.db.WithContext(c).Where("id = ? AND user_id = ?", tokenID, userID).Delete(&uModel.APIToken{}).Error
|
||||
}
|
||||
|
||||
func (r *UserRepository) UpdateNotificationTarget(c context.Context, userID int, targetID string, targetType uModel.UserNotificationType) error {
|
||||
return r.db.WithContext(c).Model(&uModel.UserNotificationTarget{}).Where("user_id = ? AND type = ?", userID, targetType).Update("target_id", targetID).Error
|
||||
func (r *UserRepository) UpdateNotificationTarget(c context.Context, userID int, targetID string, targetType nModel.NotificationType) error {
|
||||
return r.db.WithContext(c).Save(&uModel.UserNotificationTarget{
|
||||
UserID: userID,
|
||||
TargetID: targetID,
|
||||
Type: targetType,
|
||||
CreatedAt: time.Now().UTC(),
|
||||
}).Error
|
||||
}
|
||||
|
||||
func (r *UserRepository) DeleteNotificationTarget(c context.Context, userID int) error {
|
||||
return r.db.WithContext(c).Where("user_id = ?", userID).Delete(&uModel.UserNotificationTarget{}).Error
|
||||
}
|
||||
|
||||
func (r *UserRepository) UpdateNotificationTargetForAllNotifications(c context.Context, userID int, targetID string, targetType nModel.NotificationType) error {
|
||||
return r.db.WithContext(c).Model(&nModel.Notification{}).Where("user_id = ?", userID).Update("target_id", targetID).Update("type", targetType).Error
|
||||
}
|
||||
func (r *UserRepository) UpdatePasswordByUserId(c context.Context, userID int, password string) error {
|
||||
return r.db.WithContext(c).Model(&uModel.User{}).Where("id = ?", userID).Update("password", password).Error
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue