refactor: Update data models to use pointer fields for optional values and add atomic database operations for video views and user storage.

This commit is contained in:
2026-01-22 18:02:45 +07:00
parent acd0be8fa1
commit ea2edbb9e0
4 changed files with 54 additions and 22 deletions

View File

@@ -60,17 +60,17 @@ func (h *handler) Login(c *gin.Context) {
}
// Verify password (if user has password, google users might not)
if user.Password == "" {
if user.Password == nil || *user.Password == "" {
response.Error(c, http.StatusUnauthorized, "Please login with Google")
return
}
if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(req.Password)); err != nil {
if err := bcrypt.CompareHashAndPassword([]byte(*user.Password), []byte(req.Password)); err != nil {
response.Error(c, http.StatusUnauthorized, "Invalid credentials")
return
}
h.generateAndSetTokens(c, user.ID, user.Email, user.Role)
h.generateAndSetTokens(c, user.ID, user.Email, *user.Role)
response.Success(c, gin.H{"user": user})
}
@@ -111,12 +111,14 @@ func (h *handler) Register(c *gin.Context) {
return
}
password := string(hashedPassword)
role := "USER"
newUser := &model.User{
ID: uuid.New().String(),
Email: req.Email,
Password: string(hashedPassword),
Username: req.Username,
Role: "USER",
Password: &password,
Username: &req.Username,
Role: &role,
}
if err := u.WithContext(c.Request.Context()).Create(newUser); err != nil {
@@ -226,23 +228,24 @@ func (h *handler) GoogleCallback(c *gin.Context) {
u := query.User
user, err := u.WithContext(c.Request.Context()).Where(u.Email.Eq(googleUser.Email)).First()
if err != nil {
role := "USER"
user = &model.User{
ID: uuid.New().String(),
Email: googleUser.Email,
Username: googleUser.Name,
GoogleID: googleUser.ID,
Avatar: googleUser.Picture,
Role: "USER",
Username: &googleUser.Name,
GoogleID: &googleUser.ID,
Avatar: &googleUser.Picture,
Role: &role,
}
if err := u.WithContext(c.Request.Context()).Create(user); err != nil {
response.Fail(c, "Failed to create user")
return
}
} else if user.GoogleID == "" {
} else if user.GoogleID == nil || *user.GoogleID == "" {
u.WithContext(c.Request.Context()).Where(u.ID.Eq(user.ID)).Update(u.GoogleID, googleUser.ID)
}
h.generateAndSetTokens(c, user.ID, user.Email, user.Role)
h.generateAndSetTokens(c, user.ID, user.Email, *user.Role)
response.Success(c, gin.H{"user": user})
}

View File

@@ -52,13 +52,16 @@ func (h *Handler) CreatePayment(c *gin.Context) {
// In a real scenario, we would contact Stripe/PayPal here to create a session
// For now, we just create a "PENDING" payment record.
status := "PENDING"
provider := "STRIPE"
payment := &model.Payment{
ID: uuid.New().String(),
UserID: userID,
PlanID: req.PlanID,
PlanID: &req.PlanID,
Amount: req.Amount,
Status: "PENDING",
Provider: "STRIPE", // Defaulting to Stripe for this example
Status: &status,
Provider: &provider, // Defaulting to Stripe for this example
}
p := query.Payment

View File

@@ -86,21 +86,41 @@ func (h *Handler) CreateVideo(c *gin.Context) {
userID := c.GetString("userID")
status := "PUBLIC"
storageType := "S3"
video := &model.Video{
ID: uuid.New().String(),
UserID: userID,
Name: req.Title,
Title: req.Title,
Description: req.Description,
Description: &req.Description,
URL: req.URL,
Size: req.Size,
Duration: req.Duration,
Format: req.Format,
Status: "PUBLIC",
StorageType: "S3",
Status: &status,
StorageType: &storageType,
}
v := query.Video
if err := v.WithContext(c.Request.Context()).Create(video); err != nil {
q := query.Q
err := q.Transaction(func(tx *query.Query) error {
if err := tx.Video.WithContext(c.Request.Context()).Create(video); err != nil {
return err
}
// Atomic update: StorageUsed = StorageUsed + video.Size
// We use UpdateSimple with Add to ensure atomicity at database level: UPDATE users SET storage_used = storage_used + ?
if _, err := tx.User.WithContext(c.Request.Context()).
Where(tx.User.ID.Eq(userID)).
UpdateSimple(tx.User.StorageUsed.Add(video.Size)); err != nil {
return err
}
return nil
})
if err != nil {
h.logger.Error("Failed to create video record", "error", err)
response.Error(c, http.StatusInternalServerError, "Failed to create video")
return
@@ -156,6 +176,12 @@ func (h *Handler) ListVideos(c *gin.Context) {
func (h *Handler) GetVideo(c *gin.Context) {
id := c.Param("id")
v := query.Video
// Atomically increment views: UPDATE videos SET views = views + 1 WHERE id = ?
// We intentionally ignore errors here (like record not found) because the subsequent fetch will handle 404s,
// and we don't want to fail the read if writing the view count fails for some transient reason.
v.WithContext(c.Request.Context()).Where(v.ID.Eq(id)).UpdateSimple(v.Views.Add(1))
video, err := v.WithContext(c.Request.Context()).Where(v.ID.Eq(id)).First()
if err != nil {
response.Error(c, http.StatusNotFound, "Video not found")