feat(macos): show cron scheduler status
This commit is contained in:
@@ -10,6 +10,10 @@ final class CronJobsStore: ObservableObject {
|
||||
@Published var selectedJobId: String?
|
||||
@Published var runEntries: [CronRunLogEntry] = []
|
||||
|
||||
@Published var schedulerEnabled: Bool?
|
||||
@Published var schedulerStorePath: String?
|
||||
@Published var schedulerNextWakeAtMs: Int?
|
||||
|
||||
@Published var isLoadingJobs = false
|
||||
@Published var isLoadingRuns = false
|
||||
@Published var lastError: String?
|
||||
@@ -61,6 +65,11 @@ final class CronJobsStore: ObservableObject {
|
||||
defer { self.isLoadingJobs = false }
|
||||
|
||||
do {
|
||||
if let status = try? await self.fetchCronStatus() {
|
||||
self.schedulerEnabled = status.enabled
|
||||
self.schedulerStorePath = status.storePath
|
||||
self.schedulerNextWakeAtMs = status.nextWakeAtMs
|
||||
}
|
||||
let data = try await self.request(
|
||||
method: "cron.list",
|
||||
params: ["includeDisabled": true])
|
||||
@@ -205,5 +214,16 @@ final class CronJobsStore: ObservableObject {
|
||||
let rawParams = params?.reduce(into: [String: AnyCodable]()) { $0[$1.key] = AnyCodable($1.value) }
|
||||
return try await GatewayConnection.shared.request(method: method, params: rawParams, timeoutMs: timeoutMs)
|
||||
}
|
||||
|
||||
private func fetchCronStatus() async throws -> CronStatusResponse {
|
||||
let data = try await self.request(method: "cron.status", params: nil)
|
||||
return try JSONDecoder().decode(CronStatusResponse.self, from: data)
|
||||
}
|
||||
}
|
||||
|
||||
private struct CronStatusResponse: Decodable {
|
||||
let enabled: Bool
|
||||
let storePath: String
|
||||
let jobs: Int
|
||||
let nextWakeAtMs: Int?
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ struct CronSettings: View {
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
self.header
|
||||
self.schedulerBanner
|
||||
self.content
|
||||
Spacer(minLength: 0)
|
||||
}
|
||||
@@ -57,6 +58,38 @@ struct CronSettings: View {
|
||||
}
|
||||
}
|
||||
|
||||
private var schedulerBanner: some View {
|
||||
Group {
|
||||
if self.store.schedulerEnabled == false {
|
||||
VStack(alignment: .leading, spacing: 6) {
|
||||
HStack(spacing: 8) {
|
||||
Image(systemName: "exclamationmark.triangle.fill")
|
||||
.foregroundStyle(.orange)
|
||||
Text("Cron scheduler is disabled")
|
||||
.font(.headline)
|
||||
Spacer()
|
||||
}
|
||||
Text("Jobs are saved, but they will not run automatically until `cron.enabled` is set to `true` and the Gateway restarts.")
|
||||
.font(.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
if let storePath = self.store.schedulerStorePath, !storePath.isEmpty {
|
||||
Text(storePath)
|
||||
.font(.caption.monospaced())
|
||||
.foregroundStyle(.secondary)
|
||||
.textSelection(.enabled)
|
||||
.lineLimit(1)
|
||||
.truncationMode(.middle)
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(10)
|
||||
.background(Color.orange.opacity(0.10))
|
||||
.cornerRadius(8)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var header: some View {
|
||||
HStack(alignment: .top) {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
|
||||
Reference in New Issue
Block a user