draft grpc

This commit is contained in:
2026-03-13 02:17:18 +00:00
parent ea2edbb9e0
commit 91e5e3542b
116 changed files with 44505 additions and 558 deletions

View File

@@ -0,0 +1,246 @@
//go:build ignore
// +build ignore
package notifications
import (
"net/http"
"strings"
"time"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
"stream.api/internal/database/model"
"stream.api/pkg/logger"
"stream.api/pkg/response"
)
type Handler struct {
logger logger.Logger
db *gorm.DB
}
type NotificationItem struct {
ID string `json:"id"`
Type string `json:"type"`
Title string `json:"title"`
Message string `json:"message"`
Read bool `json:"read"`
ActionURL string `json:"actionUrl,omitempty"`
ActionLabel string `json:"actionLabel,omitempty"`
CreatedAt time.Time `json:"created_at"`
}
func NewHandler(l logger.Logger, db *gorm.DB) *Handler {
return &Handler{logger: l, db: db}
}
// @Summary List Notifications
// @Description Get notifications for the current user
// @Tags notifications
// @Produce json
// @Success 200 {object} response.Response
// @Failure 401 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /notifications [get]
// @Security BearerAuth
func (h *Handler) ListNotifications(c *gin.Context) {
userID := c.GetString("userID")
if userID == "" {
response.Error(c, http.StatusUnauthorized, "Unauthorized")
return
}
var rows []model.Notification
if err := h.db.WithContext(c.Request.Context()).
Where("user_id = ?", userID).
Order("created_at DESC").
Find(&rows).Error; err != nil {
h.logger.Error("Failed to list notifications", "error", err)
response.Error(c, http.StatusInternalServerError, "Failed to load notifications")
return
}
items := make([]NotificationItem, 0, len(rows))
for _, row := range rows {
items = append(items, mapNotification(row))
}
response.Success(c, gin.H{"notifications": items})
}
// @Summary Mark Notification Read
// @Description Mark a single notification as read for the current user
// @Tags notifications
// @Produce json
// @Param id path string true "Notification ID"
// @Success 200 {object} response.Response
// @Failure 401 {object} response.Response
// @Failure 404 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /notifications/{id}/read [post]
// @Security BearerAuth
func (h *Handler) MarkRead(c *gin.Context) {
h.updateReadState(c, true, false)
}
// @Summary Mark All Notifications Read
// @Description Mark all notifications as read for the current user
// @Tags notifications
// @Produce json
// @Success 200 {object} response.Response
// @Failure 401 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /notifications/read-all [post]
// @Security BearerAuth
func (h *Handler) MarkAllRead(c *gin.Context) {
userID := c.GetString("userID")
if userID == "" {
response.Error(c, http.StatusUnauthorized, "Unauthorized")
return
}
if err := h.db.WithContext(c.Request.Context()).
Model(&model.Notification{}).
Where("user_id = ? AND is_read = ?", userID, false).
Update("is_read", true).Error; err != nil {
h.logger.Error("Failed to mark all notifications as read", "error", err)
response.Error(c, http.StatusInternalServerError, "Failed to update notifications")
return
}
response.Success(c, gin.H{"message": "All notifications marked as read"})
}
// @Summary Delete Notification
// @Description Delete a single notification for the current user
// @Tags notifications
// @Produce json
// @Param id path string true "Notification ID"
// @Success 200 {object} response.Response
// @Failure 401 {object} response.Response
// @Failure 404 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /notifications/{id} [delete]
// @Security BearerAuth
func (h *Handler) DeleteNotification(c *gin.Context) {
userID := c.GetString("userID")
if userID == "" {
response.Error(c, http.StatusUnauthorized, "Unauthorized")
return
}
id := strings.TrimSpace(c.Param("id"))
if id == "" {
response.Error(c, http.StatusNotFound, "Notification not found")
return
}
result := h.db.WithContext(c.Request.Context()).
Where("id = ? AND user_id = ?", id, userID).
Delete(&model.Notification{})
if result.Error != nil {
h.logger.Error("Failed to delete notification", "error", result.Error)
response.Error(c, http.StatusInternalServerError, "Failed to delete notification")
return
}
if result.RowsAffected == 0 {
response.Error(c, http.StatusNotFound, "Notification not found")
return
}
response.Success(c, gin.H{"message": "Notification deleted"})
}
// @Summary Clear Notifications
// @Description Delete all notifications for the current user
// @Tags notifications
// @Produce json
// @Success 200 {object} response.Response
// @Failure 401 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /notifications [delete]
// @Security BearerAuth
func (h *Handler) ClearNotifications(c *gin.Context) {
userID := c.GetString("userID")
if userID == "" {
response.Error(c, http.StatusUnauthorized, "Unauthorized")
return
}
if err := h.db.WithContext(c.Request.Context()).Where("user_id = ?", userID).Delete(&model.Notification{}).Error; err != nil {
h.logger.Error("Failed to clear notifications", "error", err)
response.Error(c, http.StatusInternalServerError, "Failed to clear notifications")
return
}
response.Success(c, gin.H{"message": "All notifications deleted"})
}
func (h *Handler) updateReadState(c *gin.Context, value bool, silentNotFound bool) {
userID := c.GetString("userID")
if userID == "" {
response.Error(c, http.StatusUnauthorized, "Unauthorized")
return
}
id := strings.TrimSpace(c.Param("id"))
if id == "" {
response.Error(c, http.StatusNotFound, "Notification not found")
return
}
result := h.db.WithContext(c.Request.Context()).
Model(&model.Notification{}).
Where("id = ? AND user_id = ?", id, userID).
Update("is_read", value)
if result.Error != nil {
h.logger.Error("Failed to update notification", "error", result.Error)
response.Error(c, http.StatusInternalServerError, "Failed to update notification")
return
}
if result.RowsAffected == 0 && !silentNotFound {
response.Error(c, http.StatusNotFound, "Notification not found")
return
}
response.Success(c, gin.H{"message": "Notification updated"})
}
func mapNotification(item model.Notification) NotificationItem {
createdAt := time.Time{}
if item.CreatedAt != nil {
createdAt = item.CreatedAt.UTC()
}
return NotificationItem{
ID: item.ID,
Type: normalizeType(item.Type),
Title: item.Title,
Message: item.Message,
Read: item.IsRead,
ActionURL: model.StringValue(item.ActionURL),
ActionLabel: model.StringValue(item.ActionLabel),
CreatedAt: createdAt,
}
}
func normalizeType(value string) string {
lower := strings.ToLower(strings.TrimSpace(value))
switch {
case strings.Contains(lower, "video"):
return "video"
case strings.Contains(lower, "payment"), strings.Contains(lower, "billing"):
return "payment"
case strings.Contains(lower, "warning"):
return "warning"
case strings.Contains(lower, "error"):
return "error"
case strings.Contains(lower, "success"):
return "success"
case strings.Contains(lower, "system"):
return "system"
default:
return "info"
}
}