-
-
Notifications
You must be signed in to change notification settings - Fork 47
feat: notification system for container and image updates #730
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
bd5d30b
e68ef34
a4168bb
025c42c
768fb8a
fc43077
e688160
47ad9ee
2aa5a90
3764ba3
827f332
4df8f12
59b2fb0
5e620d6
81b1822
4e98d84
4e62bc3
dfc3525
3d31cde
41ab1ae
8f876c8
6e62118
9a6e9e0
5d080e0
2296e20
e4666aa
643b0c4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
package api | ||
|
||
import ( | ||
"net/http" | ||
|
||
"github.com/gin-gonic/gin" | ||
"github.com/ofkm/arcane-backend/internal/dto" | ||
"github.com/ofkm/arcane-backend/internal/middleware" | ||
"github.com/ofkm/arcane-backend/internal/services" | ||
) | ||
|
||
type NotificationHandler struct { | ||
notificationService *services.NotificationService | ||
} | ||
|
||
func NewNotificationHandler(group *gin.RouterGroup, notificationService *services.NotificationService, authMiddleware *middleware.AuthMiddleware) { | ||
handler := &NotificationHandler{ | ||
notificationService: notificationService, | ||
} | ||
|
||
notifications := group.Group("/environments/:id/notifications") | ||
notifications.Use(authMiddleware.WithAdminRequired().Add()) | ||
{ | ||
notifications.GET("/settings", handler.GetAllSettings) | ||
notifications.GET("/settings/:provider", handler.GetSettings) | ||
notifications.POST("/settings", handler.CreateOrUpdateSettings) | ||
notifications.DELETE("/settings/:provider", handler.DeleteSettings) | ||
notifications.POST("/test/:provider", handler.TestNotification) | ||
} | ||
} | ||
|
||
func (h *NotificationHandler) GetAllSettings(c *gin.Context) { | ||
settings, err := h.notificationService.GetAllSettings(c.Request.Context()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. style: Consider extracting environment ID from URL path if notifications should be environment-scoped: Prompt To Fix With AIThis is a comment left during a code review.
Path: backend/internal/api/notification_handler.go
Line: 33:33
Comment:
**style:** Consider extracting environment ID from URL path if notifications should be environment-scoped: `envID := c.Param("id")`
How can I resolve this? If you propose a fix, please make it concise. |
||
if err != nil { | ||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | ||
return | ||
} | ||
|
||
// Map to DTOs | ||
responses := make([]dto.NotificationSettingsResponse, len(settings)) | ||
for i, setting := range settings { | ||
responses[i] = dto.NotificationSettingsResponse{ | ||
ID: setting.ID, | ||
Provider: setting.Provider, | ||
Enabled: setting.Enabled, | ||
Config: setting.Config, | ||
} | ||
} | ||
|
||
c.JSON(http.StatusOK, responses) | ||
} | ||
|
||
func (h *NotificationHandler) GetSettings(c *gin.Context) { | ||
provider := c.Param("provider") | ||
|
||
settings, err := h.notificationService.GetSettingsByProvider(c.Request.Context(), provider) | ||
if err != nil { | ||
c.JSON(http.StatusNotFound, gin.H{"error": "Settings not found"}) | ||
return | ||
} | ||
|
||
response := dto.NotificationSettingsResponse{ | ||
ID: settings.ID, | ||
Provider: settings.Provider, | ||
Enabled: settings.Enabled, | ||
Config: settings.Config, | ||
} | ||
|
||
c.JSON(http.StatusOK, response) | ||
} | ||
|
||
func (h *NotificationHandler) CreateOrUpdateSettings(c *gin.Context) { | ||
var req dto.NotificationSettingsRequest | ||
if err := c.ShouldBindJSON(&req); err != nil { | ||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) | ||
return | ||
} | ||
|
||
settings, err := h.notificationService.CreateOrUpdateSettings( | ||
c.Request.Context(), | ||
req.Provider, | ||
req.Enabled, | ||
req.Config, | ||
) | ||
if err != nil { | ||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | ||
return | ||
} | ||
|
||
response := dto.NotificationSettingsResponse{ | ||
ID: settings.ID, | ||
Provider: settings.Provider, | ||
Enabled: settings.Enabled, | ||
Config: settings.Config, | ||
} | ||
|
||
c.JSON(http.StatusOK, response) | ||
} | ||
|
||
func (h *NotificationHandler) DeleteSettings(c *gin.Context) { | ||
provider := c.Param("provider") | ||
|
||
if err := h.notificationService.DeleteSettings(c.Request.Context(), provider); err != nil { | ||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | ||
return | ||
} | ||
|
||
c.JSON(http.StatusOK, gin.H{"message": "Settings deleted successfully"}) | ||
} | ||
|
||
func (h *NotificationHandler) TestNotification(c *gin.Context) { | ||
provider := c.Param("provider") | ||
testType := c.DefaultQuery("type", "simple") // "simple" or "image-update" | ||
|
||
if err := h.notificationService.TestNotification(c.Request.Context(), provider, testType); err != nil { | ||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | ||
return | ||
} | ||
|
||
c.JSON(http.StatusOK, gin.H{"message": "Test notification sent successfully"}) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package dto | ||
|
||
import "github.com/ofkm/arcane-backend/internal/models" | ||
|
||
type NotificationSettingsRequest struct { | ||
Provider string `json:"provider" binding:"required"` | ||
Enabled bool `json:"enabled"` | ||
Config models.JSON `json:"config" binding:"required"` | ||
} | ||
|
||
type NotificationSettingsResponse struct { | ||
ID uint `json:"id"` | ||
Provider string `json:"provider"` | ||
Enabled bool `json:"enabled"` | ||
Config models.JSON `json:"config"` | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
package models | ||
|
||
import ( | ||
"time" | ||
) | ||
|
||
type NotificationProvider string | ||
|
||
const ( | ||
NotificationProviderDiscord NotificationProvider = "discord" | ||
NotificationProviderEmail NotificationProvider = "email" | ||
) | ||
|
||
type EmailTLSMode string | ||
|
||
const ( | ||
EmailTLSModeNone EmailTLSMode = "none" | ||
EmailTLSModeStartTLS EmailTLSMode = "starttls" | ||
EmailTLSModeSSL EmailTLSMode = "ssl" | ||
) | ||
|
||
type NotificationSettings struct { | ||
ID uint `json:"id" gorm:"primaryKey"` | ||
Provider string `json:"provider" gorm:"not null;index"` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. style: Provider field should use NotificationProvider type instead of string for better type safety and validation Context Used: Rule from Follow idiomatic Go patterns and conventions Prompt To Fix With AIThis is a comment left during a code review.
Path: backend/internal/models/notification.go
Line: 24:24
Comment:
**style:** Provider field should use NotificationProvider type instead of string for better type safety and validation
**Context Used:** Rule from `dashboard` - GoLang Best Practices
Follow idiomatic Go patterns and conventions
Handle errors explicitly, don’t ... ([source](https://app.greptile.com/review/custom-context?memory=214b40a8-9695-4738-986d-5949b5d65ff1))
How can I resolve this? If you propose a fix, please make it concise. |
||
Enabled bool `json:"enabled" gorm:"default:false"` | ||
Config JSON `json:"config" gorm:"type:jsonb"` | ||
CreatedAt time.Time `json:"createdAt"` | ||
UpdatedAt time.Time `json:"updatedAt"` | ||
} | ||
|
||
func (NotificationSettings) TableName() string { | ||
return "notification_settings" | ||
} | ||
|
||
type NotificationLog struct { | ||
ID uint `json:"id" gorm:"primaryKey"` | ||
Provider string `json:"provider" gorm:"not null;index"` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. style: Provider field should use NotificationProvider type instead of string for consistency Context Used: Rule from Follow idiomatic Go patterns and conventions Prompt To Fix With AIThis is a comment left during a code review.
Path: backend/internal/models/notification.go
Line: 37:37
Comment:
**style:** Provider field should use NotificationProvider type instead of string for consistency
**Context Used:** Rule from `dashboard` - GoLang Best Practices
Follow idiomatic Go patterns and conventions
Handle errors explicitly, don’t ... ([source](https://app.greptile.com/review/custom-context?memory=214b40a8-9695-4738-986d-5949b5d65ff1))
How can I resolve this? If you propose a fix, please make it concise. |
||
ImageRef string `json:"imageRef" gorm:"not null"` | ||
Status string `json:"status" gorm:"not null"` | ||
Error *string `json:"error,omitempty"` | ||
Metadata JSON `json:"metadata" gorm:"type:jsonb"` | ||
SentAt time.Time `json:"sentAt" gorm:"not null;index"` | ||
CreatedAt time.Time `json:"createdAt"` | ||
UpdatedAt time.Time `json:"updatedAt"` | ||
} | ||
|
||
func (NotificationLog) TableName() string { | ||
return "notification_logs" | ||
} | ||
|
||
type DiscordConfig struct { | ||
WebhookURL string `json:"webhookUrl"` | ||
Username string `json:"username,omitempty"` | ||
AvatarURL string `json:"avatarUrl,omitempty"` | ||
} | ||
|
||
type EmailConfig struct { | ||
SMTPHost string `json:"smtpHost"` | ||
SMTPPort int `json:"smtpPort"` | ||
SMTPUsername string `json:"smtpUsername"` | ||
SMTPPassword string `json:"smtpPassword"` | ||
FromAddress string `json:"fromAddress"` | ||
ToAddresses []string `json:"toAddresses"` | ||
TLSMode EmailTLSMode `json:"tlsMode"` | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: The
:id
parameter from the environment route is never used in any handler methods, creating inconsistency between the API design and implementationPrompt To Fix With AI