Test Notification Processing in agent

This commit is contained in:
headlessdev 2025-04-23 21:34:52 +02:00
parent b2c47a07a6
commit 861eab8050
5 changed files with 154 additions and 25 deletions

View File

@ -145,6 +145,16 @@ func main() {
}
}()
// Check for test notifications every 10 seconds
go func() {
testNotifTicker := time.NewTicker(10 * time.Second)
defer testNotifTicker.Stop()
for range testNotifTicker.C {
checkAndSendTestNotifications(db)
}
}()
appClient := &http.Client{
Timeout: 4 * time.Second,
}
@ -735,3 +745,76 @@ func sendPushover(n Notification, message string) {
fmt.Printf("Pushover: ERROR status code: %d\n", resp.StatusCode)
}
}
func checkAndSendTestNotifications(db *sql.DB) {
// Query for test notifications
rows, err := db.Query(`SELECT tn.id, tn."notificationId" FROM test_notification tn`)
if err != nil {
fmt.Printf("Error fetching test notifications: %v\n", err)
return
}
defer rows.Close()
// Process each test notification
var testIds []int
for rows.Next() {
var id, notificationId int
if err := rows.Scan(&id, &notificationId); err != nil {
fmt.Printf("Error scanning test notification: %v\n", err)
continue
}
// Add to list of IDs to delete
testIds = append(testIds, id)
// Find the notification configuration
notifMutex.RLock()
for _, n := range notifications {
if n.ID == notificationId {
// Send test notification
fmt.Printf("Sending test notification to notification ID %d\n", notificationId)
sendSpecificNotification(n, "Test notification from CoreControl")
}
}
notifMutex.RUnlock()
}
// Delete processed test notifications
if len(testIds) > 0 {
for _, id := range testIds {
_, err := db.Exec(`DELETE FROM test_notification WHERE id = $1`, id)
if err != nil {
fmt.Printf("Error deleting test notification (ID: %d): %v\n", id, err)
}
}
}
}
func sendSpecificNotification(n Notification, message string) {
switch n.Type {
case "email":
if n.SMTPHost.Valid && n.SMTPTo.Valid {
sendEmail(n, message)
}
case "telegram":
if n.TelegramToken.Valid && n.TelegramChatID.Valid {
sendTelegram(n, message)
}
case "discord":
if n.DiscordWebhook.Valid {
sendDiscord(n, message)
}
case "gotify":
if n.GotifyUrl.Valid && n.GotifyToken.Valid {
sendGotify(n, message)
}
case "ntfy":
if n.NtfyUrl.Valid && n.NtfyToken.Valid {
sendNtfy(n, message)
}
case "pushover":
if n.PushoverUrl.Valid && n.PushoverToken.Valid && n.PushoverUser.Valid {
sendPushover(n, message)
}
}
}

View File

@ -19,7 +19,9 @@ import axios from "axios"
import Cookies from "js-cookie"
import { Button } from "@/components/ui/button"
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"
import { AlertCircle, Check, Palette, User, Bell, AtSign, Send, MessageSquare, Trash2 } from "lucide-react"
import { AlertCircle, Check, Palette, User, Bell, AtSign, Send, MessageSquare, Trash2, Play } from "lucide-react"
import { Toaster } from "@/components/ui/sonner"
import { toast } from "sonner"
import {
AlertDialog,
@ -254,6 +256,17 @@ export default function Settings() {
getNotificationText()
}, [])
const testNotification = async (id: number) => {
try {
const response = await axios.post("/api/notifications/test", {
notificationId: id,
})
toast.success("Notification will be sent in a few seconds.")
} catch (error: any) {
toast.error(error.response.data.error)
}
}
return (
<SidebarProvider>
<AppSidebar />
@ -763,6 +776,16 @@ export default function Settings() {
</p>
</div>
</div>
<div className="flex items-center gap-2">
<Button
variant="outline"
size="sm"
className="hover:bg-muted/20"
onClick={() => testNotification(notification.id)}
>
<Play className="h-4 w-4 mr-1" />
Test
</Button>
<Button
variant="ghost"
size="sm"
@ -770,9 +793,9 @@ export default function Settings() {
onClick={() => deleteNotification(notification.id)}
>
<Trash2 className="h-4 w-4 mr-1" />
Remove
</Button>
</div>
</div>
))
) : (
<div className="text-center py-12 border rounded-lg bg-muted/5">
@ -789,6 +812,7 @@ export default function Settings() {
)}
</div>
</div>
<Toaster />
</CardContent>
</Card>
</div>

25
components/ui/sonner.tsx Normal file
View File

@ -0,0 +1,25 @@
"use client"
import { useTheme } from "next-themes"
import { Toaster as Sonner, ToasterProps } from "sonner"
const Toaster = ({ ...props }: ToasterProps) => {
const { theme = "system" } = useTheme()
return (
<Sonner
theme={theme as ToasterProps["theme"]}
className="toaster group"
style={
{
"--normal-bg": "var(--popover)",
"--normal-text": "var(--popover-foreground)",
"--normal-border": "var(--border)",
} as React.CSSProperties
}
{...props}
/>
)
}
export { Toaster }

26
package-lock.json generated
View File

@ -43,6 +43,7 @@
"postcss-loader": "^8.1.1",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"sonner": "^2.0.3",
"tailwind-merge": "^3.2.0",
"tw-animate-css": "^1.2.5"
},
@ -4729,6 +4730,16 @@
"is-arrayish": "^0.3.1"
}
},
"node_modules/sonner": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.3.tgz",
"integrity": "sha512-njQ4Hht92m0sMqqHVDL32V2Oun9W1+PHO9NDv9FHfJjT3JT22IG4Jpo3FPQy+mouRKCXFWO+r67v6MrHX2zeIA==",
"license": "MIT",
"peerDependencies": {
"react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc",
"react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc"
}
},
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
@ -5062,21 +5073,6 @@
"optional": true
}
}
},
"node_modules/@next/swc-win32-x64-msvc": {
"version": "15.3.0",
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.3.0.tgz",
"integrity": "sha512-vHUQS4YVGJPmpjn7r5lEZuMhK5UQBNBRSB+iGDvJjaNk649pTIcRluDWNb9siunyLLiu/LDPHfvxBtNamyuLTw==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
}
}
}

View File

@ -44,6 +44,7 @@
"postcss-loader": "^8.1.1",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"sonner": "^2.0.3",
"tailwind-merge": "^3.2.0",
"tw-animate-css": "^1.2.5"
},