Skip to content

Commit 030e9f8

Browse files
committed
display cron warning in ui
1 parent 8e0230b commit 030e9f8

4 files changed

Lines changed: 113 additions & 4 deletions

File tree

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22

33
All notable changes to this project will be documented in this file.
44

5+
## [7.0] - 2025-09-28
6+
7+
- Add cron status monitoring endpoint `/api/cron.status`
8+
- Add SettingRepository for managing application settings
9+
- Add automatic cron health checking in frontend console
10+
- Add visual banner with setup instructions when cron is not running
11+
- Update TaskService to track last cron execution timestamp
12+
513
## [6.8] - 2025-09-24
614

715
- Fix scheduled broadcast time handling to use string format instead of time.Time

config/config.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
"github.com/spf13/viper"
1111
)
1212

13-
const VERSION = "6.8"
13+
const VERSION = "7.0"
1414

1515
type Config struct {
1616
Server ServerConfig

console/src/App.tsx

Lines changed: 103 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import { ConfigProvider, App as AntApp, ThemeConfig } from 'antd'
2-
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
1+
import { ConfigProvider, App as AntApp, ThemeConfig, Alert, Space } from 'antd'
2+
import { QueryClient, QueryClientProvider, useQuery } from '@tanstack/react-query'
33
import { RouterProvider } from '@tanstack/react-router'
44
import { router } from './router'
55
import { AuthProvider } from './contexts/AuthContext'
66
import { initializeAnalytics } from './utils/analytics-config'
7+
import { useState, useEffect } from 'react'
78

89
const queryClient = new QueryClient({
910
defaultOptions: {
@@ -50,13 +51,113 @@ const theme: ThemeConfig = {
5051
// Initialize analytics service
5152
initializeAnalytics()
5253

54+
interface CronStatusResponse {
55+
success: boolean
56+
last_run: string | null
57+
last_run_unix: number | null
58+
time_since_last_run: string | null
59+
time_since_last_run_seconds: number | null
60+
message?: string
61+
}
62+
63+
function CronStatusBanner() {
64+
const [apiEndpoint, setApiEndpoint] = useState<string>('')
65+
66+
useEffect(() => {
67+
// Get API endpoint from window object
68+
setApiEndpoint((window as any).API_ENDPOINT || '')
69+
}, [])
70+
71+
const { data: cronStatus, isError } = useQuery<CronStatusResponse>({
72+
queryKey: ['cronStatus'],
73+
queryFn: async () => {
74+
const response = await fetch(`${apiEndpoint}/api/cron.status`)
75+
if (!response.ok) {
76+
throw new Error('Failed to fetch cron status')
77+
}
78+
return response.json()
79+
},
80+
refetchInterval: 3600000, // Refetch every hour
81+
enabled: !!apiEndpoint // Only run query if we have an API endpoint
82+
})
83+
84+
// Don't show banner if we can't fetch status or if there's an error
85+
if (!cronStatus || isError) {
86+
return null
87+
}
88+
89+
// Check if last run was more than 90 seconds ago
90+
const needsCronSetup =
91+
!cronStatus.last_run ||
92+
(cronStatus.time_since_last_run_seconds && cronStatus.time_since_last_run_seconds > 90)
93+
94+
if (!needsCronSetup) {
95+
return null
96+
}
97+
98+
const cronCommand = `# Run every minute
99+
* * * * * curl ${apiEndpoint}/api/cron > /dev/null 2>&1`
100+
101+
return (
102+
<div
103+
style={{
104+
position: 'fixed',
105+
bottom: 16,
106+
right: 16,
107+
width: '500px',
108+
zIndex: 1000
109+
}}
110+
>
111+
<Alert
112+
message="Cron Job Setup Required"
113+
description={
114+
<Space direction="vertical" style={{ width: '100%' }}>
115+
<div>
116+
{cronStatus.last_run
117+
? `Last cron run was ${Math.floor((cronStatus.time_since_last_run_seconds || 0) / 60)} minutes ago. `
118+
: 'No cron run detected. '}
119+
Add this cron job to your server to enable automatic task processing:{' '}
120+
<a
121+
href="https://docs.notifuse.com/installation#a-cron-scheduler"
122+
target="_blank"
123+
rel="noopener noreferrer"
124+
style={{ color: '#7763F1' }}
125+
>
126+
Learn more
127+
</a>
128+
</div>
129+
<code
130+
style={{
131+
display: 'block',
132+
padding: '8px',
133+
backgroundColor: '#f5f5f5',
134+
border: '1px solid #d9d9d9',
135+
borderRadius: '4px',
136+
fontFamily: 'monospace',
137+
fontSize: '12px',
138+
whiteSpace: 'pre-wrap'
139+
}}
140+
>
141+
{cronCommand}
142+
</code>
143+
</Space>
144+
}
145+
type="warning"
146+
showIcon
147+
closable
148+
/>
149+
</div>
150+
)
151+
}
152+
53153
export function App() {
54154
return (
55155
<QueryClientProvider client={queryClient}>
56156
<AuthProvider>
57157
<ConfigProvider theme={theme}>
58158
<AntApp>
59159
<RouterProvider router={router} />
160+
<CronStatusBanner />
60161
</AntApp>
61162
</ConfigProvider>
62163
</AuthProvider>

console/src/layouts/WorkspaceLayout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {
1818
faChartLine
1919
} from '@fortawesome/free-solid-svg-icons'
2020
import { useAuth } from '../contexts/AuthContext'
21-
import { Workspace, WorkspaceMember, UserPermissions } from '../services/api/types'
21+
import { Workspace, UserPermissions } from '../services/api/types'
2222
import { ContactsCsvUploadProvider } from '../components/contacts/ContactsCsvUploadProvider'
2323
import { useState, useEffect } from 'react'
2424
import { FileManagerProvider } from '../components/file_manager/context'

0 commit comments

Comments
 (0)