@@ -101,6 +101,15 @@ export const notificationSchema = z.discriminatedUnion("type", [
101
101
decoration : z . boolean ( ) . default ( true ) ,
102
102
} )
103
103
. merge ( notificationBaseSchema ) ,
104
+ z
105
+ . object ( {
106
+ type : z . literal ( "ntfy" ) ,
107
+ serverUrl : z . string ( ) . min ( 1 , { message : "Server URL is required" } ) ,
108
+ topic : z . string ( ) . min ( 1 , { message : "Topic is required" } ) ,
109
+ accessToken : z . string ( ) . min ( 1 , { message : "Access Token is required" } ) ,
110
+ priority : z . number ( ) . min ( 1 ) . max ( 5 ) . default ( 3 ) ,
111
+ } )
112
+ . merge ( notificationBaseSchema ) ,
104
113
] ) ;
105
114
106
115
export const notificationsMap = {
@@ -124,6 +133,10 @@ export const notificationsMap = {
124
133
icon : < MessageCircleMore size = { 29 } className = "text-muted-foreground" /> ,
125
134
label : "Gotify" ,
126
135
} ,
136
+ ntfy : {
137
+ icon : < MessageCircleMore size = { 29 } className = "text-muted-foreground" /> ,
138
+ label : "ntfy" ,
139
+ } ,
127
140
} ;
128
141
129
142
export type NotificationSchema = z . infer < typeof notificationSchema > ;
@@ -155,6 +168,8 @@ export const HandleNotifications = ({ notificationId }: Props) => {
155
168
api . notification . testEmailConnection . useMutation ( ) ;
156
169
const { mutateAsync : testGotifyConnection , isLoading : isLoadingGotify } =
157
170
api . notification . testGotifyConnection . useMutation ( ) ;
171
+ const { mutateAsync : testNtfyConnection , isLoading : isLoadingNtfy } =
172
+ api . notification . testNtfyConnection . useMutation ( ) ;
158
173
const slackMutation = notificationId
159
174
? api . notification . updateSlack . useMutation ( )
160
175
: api . notification . createSlack . useMutation ( ) ;
@@ -170,6 +185,9 @@ export const HandleNotifications = ({ notificationId }: Props) => {
170
185
const gotifyMutation = notificationId
171
186
? api . notification . updateGotify . useMutation ( )
172
187
: api . notification . createGotify . useMutation ( ) ;
188
+ const ntfyMutation = notificationId
189
+ ? api . notification . updateNtfy . useMutation ( )
190
+ : api . notification . createNtfy . useMutation ( ) ;
173
191
174
192
const form = useForm < NotificationSchema > ( {
175
193
defaultValues : {
@@ -266,6 +284,20 @@ export const HandleNotifications = ({ notificationId }: Props) => {
266
284
name : notification . name ,
267
285
dockerCleanup : notification . dockerCleanup ,
268
286
} ) ;
287
+ } else if ( notification . notificationType === "ntfy" ) {
288
+ form . reset ( {
289
+ appBuildError : notification . appBuildError ,
290
+ appDeploy : notification . appDeploy ,
291
+ dokployRestart : notification . dokployRestart ,
292
+ databaseBackup : notification . databaseBackup ,
293
+ type : notification . notificationType ,
294
+ accessToken : notification . ntfy ?. accessToken ,
295
+ topic : notification . ntfy ?. topic ,
296
+ priority : notification . ntfy ?. priority ,
297
+ serverUrl : notification . ntfy ?. serverUrl ,
298
+ name : notification . name ,
299
+ dockerCleanup : notification . dockerCleanup ,
300
+ } ) ;
269
301
}
270
302
} else {
271
303
form . reset ( ) ;
@@ -278,6 +310,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
278
310
discord : discordMutation ,
279
311
email : emailMutation ,
280
312
gotify : gotifyMutation ,
313
+ ntfy : ntfyMutation ,
281
314
} ;
282
315
283
316
const onSubmit = async ( data : NotificationSchema ) => {
@@ -366,6 +399,21 @@ export const HandleNotifications = ({ notificationId }: Props) => {
366
399
notificationId : notificationId || "" ,
367
400
gotifyId : notification ?. gotifyId || "" ,
368
401
} ) ;
402
+ } else if ( data . type === "ntfy" ) {
403
+ promise = ntfyMutation . mutateAsync ( {
404
+ appBuildError : appBuildError ,
405
+ appDeploy : appDeploy ,
406
+ dokployRestart : dokployRestart ,
407
+ databaseBackup : databaseBackup ,
408
+ serverUrl : data . serverUrl ,
409
+ accessToken : data . accessToken ,
410
+ topic : data . topic ,
411
+ priority : data . priority ,
412
+ name : data . name ,
413
+ dockerCleanup : dockerCleanup ,
414
+ notificationId : notificationId || "" ,
415
+ ntfyId : notification ?. ntfyId || "" ,
416
+ } ) ;
369
417
}
370
418
371
419
if ( promise ) {
@@ -875,6 +923,83 @@ export const HandleNotifications = ({ notificationId }: Props) => {
875
923
/>
876
924
</ >
877
925
) }
926
+
927
+ { type === "ntfy" && (
928
+ < >
929
+ < FormField
930
+ control = { form . control }
931
+ name = "serverUrl"
932
+ render = { ( { field } ) => (
933
+ < FormItem >
934
+ < FormLabel > Server URL</ FormLabel >
935
+ < FormControl >
936
+ < Input placeholder = "https://ntfy.sh" { ...field } />
937
+ </ FormControl >
938
+ < FormMessage />
939
+ </ FormItem >
940
+ ) }
941
+ />
942
+ < FormField
943
+ control = { form . control }
944
+ name = "topic"
945
+ render = { ( { field } ) => (
946
+ < FormItem >
947
+ < FormLabel > Topic</ FormLabel >
948
+ < FormControl >
949
+ < Input placeholder = "deployments" { ...field } />
950
+ </ FormControl >
951
+ < FormMessage />
952
+ </ FormItem >
953
+ ) }
954
+ />
955
+ < FormField
956
+ control = { form . control }
957
+ name = "accessToken"
958
+ render = { ( { field } ) => (
959
+ < FormItem >
960
+ < FormLabel > Access Token</ FormLabel >
961
+ < FormControl >
962
+ < Input
963
+ placeholder = "AzxcvbnmKjhgfdsa..."
964
+ { ...field }
965
+ />
966
+ </ FormControl >
967
+ < FormMessage />
968
+ </ FormItem >
969
+ ) }
970
+ />
971
+ < FormField
972
+ control = { form . control }
973
+ name = "priority"
974
+ defaultValue = { 3 }
975
+ render = { ( { field } ) => (
976
+ < FormItem className = "w-full" >
977
+ < FormLabel > Priority</ FormLabel >
978
+ < FormControl >
979
+ < Input
980
+ placeholder = "3"
981
+ { ...field }
982
+ onChange = { ( e ) => {
983
+ const value = e . target . value ;
984
+ if ( value ) {
985
+ const port = Number . parseInt ( value ) ;
986
+ if ( port > 0 && port <= 5 ) {
987
+ field . onChange ( port ) ;
988
+ }
989
+ }
990
+ } }
991
+ type = "number"
992
+ />
993
+ </ FormControl >
994
+ < FormDescription >
995
+ Message priority (1-5, default: 3)
996
+ </ FormDescription >
997
+ < FormMessage />
998
+ </ FormItem >
999
+ ) }
1000
+ />
1001
+ </ >
1002
+ ) }
878
1003
</ div >
879
1004
</ div >
880
1005
< div className = "flex flex-col gap-4" >
@@ -1024,7 +1149,8 @@ export const HandleNotifications = ({ notificationId }: Props) => {
1024
1149
isLoadingTelegram ||
1025
1150
isLoadingDiscord ||
1026
1151
isLoadingEmail ||
1027
- isLoadingGotify
1152
+ isLoadingGotify ||
1153
+ isLoadingNtfy
1028
1154
}
1029
1155
variant = "secondary"
1030
1156
onClick = { async ( ) => {
@@ -1061,6 +1187,13 @@ export const HandleNotifications = ({ notificationId }: Props) => {
1061
1187
priority : form . getValues ( "priority" ) ,
1062
1188
decoration : form . getValues ( "decoration" ) ,
1063
1189
} ) ;
1190
+ } else if ( type === "ntfy" ) {
1191
+ await testNtfyConnection ( {
1192
+ serverUrl : form . getValues ( "serverUrl" ) ,
1193
+ topic : form . getValues ( "topic" ) ,
1194
+ accessToken : form . getValues ( "accessToken" ) ,
1195
+ priority : form . getValues ( "priority" ) ,
1196
+ } ) ;
1064
1197
}
1065
1198
toast . success ( "Connection Success" ) ;
1066
1199
} catch {
0 commit comments