@wakastellar/ui 2.4.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/blocks/antivirus-dashboard/index.d.ts +44 -0
- package/dist/blocks/clamav-service-status/index.d.ts +35 -0
- package/dist/blocks/file-scan-uploader/index.d.ts +29 -0
- package/dist/blocks/index.d.ts +18 -9
- package/dist/blocks/quarantine-manager/index.d.ts +27 -0
- package/dist/blocks/scan-history-log/index.d.ts +28 -0
- package/dist/blocks/scan-policy-editor/index.d.ts +27 -0
- package/dist/blocks/scan-report-generator/index.d.ts +47 -0
- package/dist/blocks/signature-database-manager/index.d.ts +39 -0
- package/dist/blocks/threat-alert-banner/index.d.ts +26 -0
- package/dist/components/index.d.ts +4 -4
- package/dist/components/waka-signature-pad/index.d.ts +1 -1
- package/dist/exceljs.min-BcLLX0PC.js +29 -0
- package/dist/exceljs.min-KOayaaQ4.mjs +23013 -0
- package/dist/export.cjs.js +1 -1
- package/dist/export.d.ts +2 -2
- package/dist/export.es.js +1 -1
- package/dist/index.cjs.js +136 -136
- package/dist/index.es.js +29978 -27215
- package/dist/stories/Button.stories.d.ts +1 -1
- package/dist/stories/Header.stories.d.ts +1 -1
- package/dist/stories/Page.stories.d.ts +1 -1
- package/dist/useDataTableImport-COVnvslz.js +9 -0
- package/dist/useDataTableImport-DAlxBY8w.mjs +237 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/logger.d.ts +9 -0
- package/package.json +6 -5
- package/src/blocks/antivirus-dashboard/AntivirusDashboard.stories.tsx +291 -0
- package/src/blocks/antivirus-dashboard/index.tsx +525 -0
- package/src/blocks/clamav-service-status/ClamAVServiceStatus.stories.tsx +195 -0
- package/src/blocks/clamav-service-status/index.tsx +370 -0
- package/src/blocks/file-scan-uploader/FileScanUploader.stories.tsx +257 -0
- package/src/blocks/file-scan-uploader/index.tsx +311 -0
- package/src/blocks/index.ts +163 -11
- package/src/blocks/quarantine-manager/QuarantineManager.stories.tsx +209 -0
- package/src/blocks/quarantine-manager/index.tsx +435 -0
- package/src/blocks/scan-history-log/ScanHistoryLog.stories.tsx +231 -0
- package/src/blocks/scan-history-log/index.tsx +406 -0
- package/src/blocks/scan-policy-editor/ScanPolicyEditor.stories.tsx +106 -0
- package/src/blocks/scan-policy-editor/index.tsx +418 -0
- package/src/blocks/scan-report-generator/ScanReportGenerator.stories.tsx +232 -0
- package/src/blocks/scan-report-generator/index.tsx +612 -0
- package/src/blocks/sidebar/index.tsx +2 -1
- package/src/blocks/signature-database-manager/SignatureDatabaseManager.stories.tsx +279 -0
- package/src/blocks/signature-database-manager/index.tsx +470 -0
- package/src/blocks/theme-creator-block/index.tsx +16 -2
- package/src/blocks/threat-alert-banner/ThreatAlertBanner.stories.tsx +152 -0
- package/src/blocks/threat-alert-banner/index.tsx +320 -0
- package/src/components/DataTable/DataTable.stories.tsx +203 -0
- package/src/components/DataTable/hooks/useDataTableExport.ts +38 -31
- package/src/components/DataTable/hooks/useDataTableImport.ts +31 -20
- package/src/components/error-boundary/ErrorBoundary.stories.tsx +125 -0
- package/src/components/index.ts +45 -4
- package/src/components/language-selector/LanguageSelector.stories.tsx +112 -0
- package/src/components/theme-selector/ThemeSelector.stories.tsx +77 -0
- package/src/components/toaster/Toaster.stories.tsx +67 -0
- package/src/components/waka-activity-feed/WakaActivityFeed.stories.tsx +116 -0
- package/src/components/waka-ad-banner/WakaAdBanner.stories.tsx +102 -0
- package/src/components/waka-ad-fallback/WakaAdFallback.stories.tsx +117 -0
- package/src/components/waka-ad-inline/WakaAdInline.stories.tsx +105 -0
- package/src/components/waka-ad-interstitial/WakaAdInterstitial.stories.tsx +92 -0
- package/src/components/waka-ad-placeholder/WakaAdPlaceholder.stories.tsx +89 -0
- package/src/components/waka-ad-provider/WakaAdProvider.stories.tsx +110 -0
- package/src/components/waka-ad-sidebar/WakaAdSidebar.stories.tsx +89 -0
- package/src/components/waka-ad-sidebar/index.tsx +3 -2
- package/src/components/waka-ad-sticky-footer/WakaAdStickyFooter.stories.tsx +88 -0
- package/src/components/waka-address-autocomplete/WakaAddressAutocomplete.stories.tsx +46 -0
- package/src/components/waka-admincrumb/WakaAdmincrumb.stories.tsx +166 -0
- package/src/components/waka-alert-panel/WakaAlertPanel.stories.tsx +45 -0
- package/src/components/waka-alert-stack/WakaAlertStack.stories.tsx +62 -0
- package/src/components/waka-allocation-matrix/WakaAllocationMatrix.stories.tsx +68 -0
- package/src/components/waka-approval-chain/WakaApprovalChain.stories.tsx +63 -0
- package/src/components/waka-audit-log/WakaAuditLog.stories.tsx +73 -0
- package/src/components/waka-autocomplete/WakaAutocomplete.stories.tsx +132 -172
- package/src/components/waka-biometric-prompt/WakaBiometricPrompt.stories.tsx +48 -0
- package/src/components/waka-breadcrumb/WakaBreadcrumb.stories.tsx +74 -191
- package/src/components/waka-breadcrumb-path/WakaBreadcrumbPath.stories.tsx +40 -0
- package/src/components/waka-budget-burn/WakaBudgetBurn.stories.tsx +86 -0
- package/src/components/waka-capacity-planner/WakaCapacityPlanner.stories.tsx +273 -0
- package/src/components/waka-cart-summary/WakaCartSummary.stories.tsx +176 -0
- package/src/components/waka-cart-summary/index.tsx +19 -10
- package/src/components/waka-challenge-timer/WakaChallengeTimer.stories.tsx +98 -0
- package/src/components/waka-chat-bubble/WakaChatBubble.stories.tsx +118 -0
- package/src/components/waka-checklist/WakaChecklist.stories.tsx +71 -0
- package/src/components/waka-checkout-stepper/WakaCheckoutStepper.stories.tsx +102 -0
- package/src/components/waka-cohort-table/WakaCohortTable.stories.tsx +56 -0
- package/src/components/waka-color-picker/WakaColorPicker.stories.tsx +99 -155
- package/src/components/waka-combo-counter/WakaComboCounter.stories.tsx +128 -0
- package/src/components/waka-command-bar/WakaCommandBar.stories.tsx +45 -0
- package/src/components/waka-compare-period/WakaComparePeriod.stories.tsx +76 -0
- package/src/components/waka-config-comparator/WakaConfigComparator.stories.tsx +143 -0
- package/src/components/waka-connection-matrix/WakaConnectionMatrix.stories.tsx +52 -0
- package/src/components/waka-content-recommendation/WakaContentRecommendation.stories.tsx +41 -0
- package/src/components/waka-coupon-input/WakaCouponInput.stories.tsx +126 -0
- package/src/components/waka-credit-card-input/WakaCreditCardInput.stories.tsx +120 -0
- package/src/components/waka-datetime-picker.form-integration/WakaDateTimePickerForm.stories.tsx +79 -0
- package/src/components/waka-dependency-tree/WakaDependencyTree.stories.tsx +72 -0
- package/src/components/waka-device-trust/WakaDeviceTrust.stories.tsx +109 -0
- package/src/components/waka-empty-state/WakaEmptyState.stories.tsx +87 -0
- package/src/components/waka-feature-announcement/WakaFeatureAnnouncement.stories.tsx +47 -0
- package/src/components/waka-feature-flag-row/WakaFeatureFlagRow.stories.tsx +188 -0
- package/src/components/waka-file-upload/WakaFileUpload.stories.tsx +118 -174
- package/src/components/waka-floating-nav/WakaFloatingNav.stories.tsx +53 -0
- package/src/components/waka-goal-progress/WakaGoalProgress.stories.tsx +137 -0
- package/src/components/waka-hotspot/WakaHotspot.stories.tsx +56 -0
- package/src/components/waka-invoice-preview/WakaInvoicePreview.stories.tsx +169 -0
- package/src/components/waka-kpi-dashboard/WakaKpiDashboard.stories.tsx +46 -0
- package/src/components/waka-level-progress/WakaLevelProgress.stories.tsx +94 -75
- package/src/components/waka-liquid-button/WakaLiquidButton.stories.tsx +45 -0
- package/src/components/waka-magic-link/WakaMagicLink.stories.tsx +61 -0
- package/src/components/waka-magnetic-button/WakaMagneticButton.stories.tsx +40 -0
- package/src/components/waka-mention-input/WakaMentionInput.stories.tsx +140 -0
- package/src/components/waka-milestone-road/WakaMilestoneRoad.stories.tsx +143 -0
- package/src/components/waka-orbital-menu/WakaOrbitalMenu.stories.tsx +54 -0
- package/src/components/waka-order-tracker/WakaOrderTracker.stories.tsx +163 -0
- package/src/components/waka-outstream-video/WakaOutstreamVideo.stories.tsx +94 -0
- package/src/components/waka-pagination/WakaPagination.stories.tsx +110 -280
- package/src/components/waka-password-strength/WakaPasswordStrength.stories.tsx +132 -268
- package/src/components/waka-payment-method-picker/WakaPaymentMethodPicker.stories.tsx +141 -0
- package/src/components/waka-permission-matrix/WakaPermissionMatrix.stories.tsx +124 -0
- package/src/components/waka-phone-input/WakaPhoneInput.stories.tsx +56 -0
- package/src/components/waka-points-popup/WakaPointsPopup.stories.tsx +96 -0
- package/src/components/waka-power-up/WakaPowerUp.stories.tsx +121 -0
- package/src/components/waka-presence-indicator/WakaPresenceIndicator.stories.tsx +49 -0
- package/src/components/waka-pricing-table/WakaPricingTable.stories.tsx +159 -0
- package/src/components/waka-product-card/WakaProductCard.stories.tsx +202 -0
- package/src/components/waka-progress-onboarding/WakaProgressOnboarding.stories.tsx +57 -0
- package/src/components/waka-pull-to-refresh/WakaPullToRefresh.stories.tsx +51 -0
- package/src/components/waka-rank-badge/WakaRankBadge.stories.tsx +108 -0
- package/src/components/waka-rating-input/WakaRatingInput.stories.tsx +51 -0
- package/src/components/waka-reaction-picker/WakaReactionPicker.stories.tsx +52 -0
- package/src/components/waka-region-map/WakaRegionMap.stories.tsx +181 -0
- package/src/components/waka-resource-pool/WakaResourcePool.stories.tsx +70 -0
- package/src/components/waka-rich-text-editor/WakaRichTextEditor.stories.tsx +108 -197
- package/src/components/waka-rollback-slider/WakaRollbackSlider.stories.tsx +41 -0
- package/src/components/waka-schedule-picker/WakaSchedulePicker.stories.tsx +64 -0
- package/src/components/waka-season-pass/WakaSeasonPass.stories.tsx +107 -0
- package/src/components/waka-security-scan-result/WakaSecurityScanResult.stories.tsx +146 -0
- package/src/components/waka-security-score/WakaSecurityScore.stories.tsx +63 -0
- package/src/components/waka-session-manager/WakaSessionManager.stories.tsx +68 -0
- package/src/components/waka-signature-pad/WakaSignaturePad.stories.tsx +159 -0
- package/src/components/waka-signature-pad/index.tsx +5 -3
- package/src/components/waka-sla-tracker/WakaSlaTracker.stories.tsx +65 -0
- package/src/components/waka-slider-range/WakaSliderRange.stories.tsx +66 -0
- package/src/components/waka-sponsored-badge/WakaSponsoredBadge.stories.tsx +60 -0
- package/src/components/waka-sponsored-card/WakaSponsoredCard.stories.tsx +64 -0
- package/src/components/waka-sponsored-feed/WakaSponsoredFeed.stories.tsx +58 -0
- package/src/components/waka-spotlight/WakaSpotlight.stories.tsx +53 -0
- package/src/components/waka-stats-hexagon/WakaStatsHexagon.stories.tsx +161 -0
- package/src/components/waka-stepper/WakaStepper.stories.tsx +137 -410
- package/src/components/waka-swipe-card/WakaSwipeCard.stories.tsx +51 -0
- package/src/components/waka-tag-input/WakaTagInput.stories.tsx +224 -0
- package/src/components/waka-team-banner/WakaTeamBanner.stories.tsx +50 -0
- package/src/components/waka-theme-creator/WakaThemeCreator.stories.tsx +58 -0
- package/src/components/waka-theme-manager/WakaThemeManager.stories.tsx +298 -0
- package/src/components/waka-theme-manager/index.tsx +6 -11
- package/src/components/waka-thread-view/WakaThreadView.stories.tsx +143 -0
- package/src/components/waka-timeline/WakaTimeline.stories.tsx +171 -324
- package/src/components/waka-tooltip-tour/WakaTooltipTour.stories.tsx +92 -0
- package/src/components/waka-tour-guide/WakaTourGuide.stories.tsx +89 -0
- package/src/components/waka-treemap-chart/WakaTreemapChart.stories.tsx +234 -129
- package/src/components/waka-treemap-chart/index.tsx +2 -2
- package/src/components/waka-two-factor-setup/WakaTwoFactorSetup.stories.tsx +142 -0
- package/src/components/waka-typing-indicator/WakaTypingIndicator.stories.tsx +134 -0
- package/src/components/waka-video-ad/WakaVideoAd.stories.tsx +138 -0
- package/src/components/waka-video-call/WakaVideoCall.stories.tsx +186 -0
- package/src/components/waka-video-overlay/WakaVideoOverlay.stories.tsx +100 -0
- package/src/components/waka-voice-message/WakaVoiceMessage.stories.tsx +190 -0
- package/src/components/waka-welcome-modal/WakaWelcomeModal.stories.tsx +87 -0
- package/src/components/waka-xp-bar/WakaXPBar.stories.tsx +29 -29
- package/dist/useDataTableImport-D8R2HQl6.mjs +0 -229
- package/dist/useDataTableImport-S_hhA5Wo.js +0 -9
- package/src/components/DataTable/README.md +0 -446
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react"
|
|
2
|
+
import { ClamAVServiceStatus } from "./index"
|
|
3
|
+
|
|
4
|
+
const meta = {
|
|
5
|
+
title: "Blocks/Antivirus/ClamAVServiceStatus",
|
|
6
|
+
component: ClamAVServiceStatus,
|
|
7
|
+
parameters: {
|
|
8
|
+
layout: "padded"
|
|
9
|
+
},
|
|
10
|
+
tags: ["autodocs"]
|
|
11
|
+
} satisfies Meta<typeof ClamAVServiceStatus>
|
|
12
|
+
|
|
13
|
+
export default meta
|
|
14
|
+
type Story = StoryObj<typeof meta>
|
|
15
|
+
|
|
16
|
+
const generateUptimeHistory = (pattern: "healthy" | "degraded") => {
|
|
17
|
+
const hours = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
|
|
18
|
+
|
|
19
|
+
if (pattern === "healthy") {
|
|
20
|
+
return hours.map(hour => ({
|
|
21
|
+
hour,
|
|
22
|
+
status: Math.random() > 0.95 ? ("degraded" as const) : ("up" as const)
|
|
23
|
+
}))
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return hours.map(hour => ({
|
|
27
|
+
hour,
|
|
28
|
+
status: Math.random() > 0.7 ? ("up" as const) : Math.random() > 0.5 ? ("degraded" as const) : ("down" as const)
|
|
29
|
+
}))
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const Healthy: Story = {
|
|
33
|
+
args: {
|
|
34
|
+
status: "running",
|
|
35
|
+
version: "1.0.3",
|
|
36
|
+
uptime: 259200, // 3 days
|
|
37
|
+
metrics: {
|
|
38
|
+
cpuUsage: 2.5,
|
|
39
|
+
memoryUsage: 512,
|
|
40
|
+
threads: 4,
|
|
41
|
+
scanQueue: 0,
|
|
42
|
+
activeConnections: 3
|
|
43
|
+
},
|
|
44
|
+
signatures: {
|
|
45
|
+
version: "27456",
|
|
46
|
+
count: 8764532,
|
|
47
|
+
lastUpdate: new Date(Date.now() - 3600000), // 1h ago
|
|
48
|
+
freshclamStatus: "running",
|
|
49
|
+
nextScheduledUpdate: new Date(Date.now() + 3600000) // in 1h
|
|
50
|
+
},
|
|
51
|
+
socketType: "tcp",
|
|
52
|
+
socketPath: "3310",
|
|
53
|
+
uptimeHistory: generateUptimeHistory("healthy"),
|
|
54
|
+
onRestart: () => console.log("Restart clicked"),
|
|
55
|
+
onUpdateSignatures: () => console.log("Update signatures clicked"),
|
|
56
|
+
onViewLogs: () => console.log("View logs clicked")
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export const Degraded: Story = {
|
|
61
|
+
args: {
|
|
62
|
+
status: "running",
|
|
63
|
+
version: "1.0.3",
|
|
64
|
+
uptime: 86400, // 1 day
|
|
65
|
+
metrics: {
|
|
66
|
+
cpuUsage: 78.3,
|
|
67
|
+
memoryUsage: 892,
|
|
68
|
+
threads: 8,
|
|
69
|
+
scanQueue: 12,
|
|
70
|
+
activeConnections: 15
|
|
71
|
+
},
|
|
72
|
+
signatures: {
|
|
73
|
+
version: "27450",
|
|
74
|
+
count: 8762104,
|
|
75
|
+
lastUpdate: new Date(Date.now() - 172800000), // 2 days ago
|
|
76
|
+
freshclamStatus: "error",
|
|
77
|
+
nextScheduledUpdate: new Date(Date.now() + 3600000)
|
|
78
|
+
},
|
|
79
|
+
socketType: "unix",
|
|
80
|
+
socketPath: "/var/run/clamav/clamd.sock",
|
|
81
|
+
uptimeHistory: generateUptimeHistory("degraded"),
|
|
82
|
+
onRestart: () => console.log("Restart clicked"),
|
|
83
|
+
onUpdateSignatures: () => console.log("Update signatures clicked"),
|
|
84
|
+
onViewLogs: () => console.log("View logs clicked")
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export const Stopped: Story = {
|
|
89
|
+
args: {
|
|
90
|
+
status: "stopped",
|
|
91
|
+
version: "1.0.3",
|
|
92
|
+
uptime: 0,
|
|
93
|
+
metrics: {
|
|
94
|
+
cpuUsage: 0,
|
|
95
|
+
memoryUsage: 0,
|
|
96
|
+
threads: 0,
|
|
97
|
+
scanQueue: 0,
|
|
98
|
+
activeConnections: 0
|
|
99
|
+
},
|
|
100
|
+
signatures: {
|
|
101
|
+
version: "27456",
|
|
102
|
+
count: 8764532,
|
|
103
|
+
lastUpdate: new Date(Date.now() - 7200000), // 2h ago
|
|
104
|
+
freshclamStatus: "stopped"
|
|
105
|
+
},
|
|
106
|
+
socketType: "tcp",
|
|
107
|
+
socketPath: "3310",
|
|
108
|
+
uptimeHistory: [],
|
|
109
|
+
onRestart: () => console.log("Restart clicked"),
|
|
110
|
+
onUpdateSignatures: () => console.log("Update signatures clicked"),
|
|
111
|
+
onViewLogs: () => console.log("View logs clicked")
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export const Error: Story = {
|
|
116
|
+
args: {
|
|
117
|
+
status: "error",
|
|
118
|
+
version: "1.0.3",
|
|
119
|
+
uptime: 0,
|
|
120
|
+
metrics: {
|
|
121
|
+
cpuUsage: 0,
|
|
122
|
+
memoryUsage: 128,
|
|
123
|
+
threads: 1,
|
|
124
|
+
scanQueue: 0,
|
|
125
|
+
activeConnections: 0
|
|
126
|
+
},
|
|
127
|
+
signatures: {
|
|
128
|
+
version: "27456",
|
|
129
|
+
count: 8764532,
|
|
130
|
+
lastUpdate: new Date(Date.now() - 86400000), // 1 day ago
|
|
131
|
+
freshclamStatus: "error"
|
|
132
|
+
},
|
|
133
|
+
socketType: "tcp",
|
|
134
|
+
socketPath: "3310",
|
|
135
|
+
uptimeHistory: generateUptimeHistory("degraded"),
|
|
136
|
+
onRestart: () => console.log("Restart clicked"),
|
|
137
|
+
onUpdateSignatures: () => console.log("Update signatures clicked"),
|
|
138
|
+
onViewLogs: () => console.log("View logs clicked")
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export const Restarting: Story = {
|
|
143
|
+
args: {
|
|
144
|
+
status: "restarting",
|
|
145
|
+
version: "1.0.3",
|
|
146
|
+
uptime: 0,
|
|
147
|
+
metrics: {
|
|
148
|
+
cpuUsage: 15.2,
|
|
149
|
+
memoryUsage: 256,
|
|
150
|
+
threads: 2,
|
|
151
|
+
scanQueue: 0,
|
|
152
|
+
activeConnections: 0
|
|
153
|
+
},
|
|
154
|
+
signatures: {
|
|
155
|
+
version: "27456",
|
|
156
|
+
count: 8764532,
|
|
157
|
+
lastUpdate: new Date(Date.now() - 3600000),
|
|
158
|
+
freshclamStatus: "stopped"
|
|
159
|
+
},
|
|
160
|
+
socketType: "tcp",
|
|
161
|
+
socketPath: "3310",
|
|
162
|
+
uptimeHistory: generateUptimeHistory("healthy"),
|
|
163
|
+
onRestart: () => console.log("Restart clicked"),
|
|
164
|
+
onUpdateSignatures: () => console.log("Update signatures clicked"),
|
|
165
|
+
onViewLogs: () => console.log("View logs clicked")
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export const UpdatingSignatures: Story = {
|
|
170
|
+
args: {
|
|
171
|
+
status: "running",
|
|
172
|
+
version: "1.0.3",
|
|
173
|
+
uptime: 172800, // 2 days
|
|
174
|
+
metrics: {
|
|
175
|
+
cpuUsage: 45.8,
|
|
176
|
+
memoryUsage: 678,
|
|
177
|
+
threads: 6,
|
|
178
|
+
scanQueue: 3,
|
|
179
|
+
activeConnections: 5
|
|
180
|
+
},
|
|
181
|
+
signatures: {
|
|
182
|
+
version: "27456",
|
|
183
|
+
count: 8764532,
|
|
184
|
+
lastUpdate: new Date(Date.now() - 1800000), // 30min ago
|
|
185
|
+
freshclamStatus: "updating",
|
|
186
|
+
nextScheduledUpdate: new Date(Date.now() + 1800000)
|
|
187
|
+
},
|
|
188
|
+
socketType: "tcp",
|
|
189
|
+
socketPath: "3310",
|
|
190
|
+
uptimeHistory: generateUptimeHistory("healthy"),
|
|
191
|
+
onRestart: () => console.log("Restart clicked"),
|
|
192
|
+
onUpdateSignatures: () => console.log("Update signatures clicked"),
|
|
193
|
+
onViewLogs: () => console.log("View logs clicked")
|
|
194
|
+
}
|
|
195
|
+
}
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { Activity, AlertTriangle, CheckCircle, Clock, Database, HardDrive, Loader2, RefreshCw, Terminal, Wifi } from "lucide-react"
|
|
4
|
+
import { Badge } from "../../components/badge"
|
|
5
|
+
import { Button } from "../../components/button"
|
|
6
|
+
import { Card } from "../../components/card"
|
|
7
|
+
import { Progress } from "../../components/progress"
|
|
8
|
+
import { cn } from "../../utils/cn"
|
|
9
|
+
|
|
10
|
+
export type DaemonStatus = "running" | "stopped" | "error" | "restarting"
|
|
11
|
+
export type FreshclamStatus = "running" | "stopped" | "error" | "updating"
|
|
12
|
+
|
|
13
|
+
export interface ClamAVMetrics {
|
|
14
|
+
cpuUsage: number
|
|
15
|
+
memoryUsage: number
|
|
16
|
+
threads: number
|
|
17
|
+
scanQueue: number
|
|
18
|
+
activeConnections: number
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface SignatureInfo {
|
|
22
|
+
version: string
|
|
23
|
+
count: number
|
|
24
|
+
lastUpdate: Date
|
|
25
|
+
freshclamStatus: FreshclamStatus
|
|
26
|
+
nextScheduledUpdate?: Date
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface UptimeEntry {
|
|
30
|
+
hour: string
|
|
31
|
+
status: "up" | "degraded" | "down"
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface ClamAVServiceStatusProps {
|
|
35
|
+
status?: DaemonStatus
|
|
36
|
+
version?: string
|
|
37
|
+
uptime?: number // seconds
|
|
38
|
+
metrics?: ClamAVMetrics
|
|
39
|
+
signatures?: SignatureInfo
|
|
40
|
+
socketType?: "tcp" | "unix"
|
|
41
|
+
socketPath?: string
|
|
42
|
+
uptimeHistory?: UptimeEntry[]
|
|
43
|
+
onRestart?: () => void
|
|
44
|
+
onUpdateSignatures?: () => void
|
|
45
|
+
onViewLogs?: () => void
|
|
46
|
+
className?: string
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const formatUptime = (seconds: number): string => {
|
|
50
|
+
const days = Math.floor(seconds / 86400)
|
|
51
|
+
const hours = Math.floor((seconds % 86400) / 3600)
|
|
52
|
+
const minutes = Math.floor((seconds % 3600) / 60)
|
|
53
|
+
|
|
54
|
+
if (days > 0) return `${days}d ${hours}h`
|
|
55
|
+
if (hours > 0) return `${hours}h ${minutes}m`
|
|
56
|
+
return `${minutes}m`
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const formatDate = (date: Date): string => {
|
|
60
|
+
const now = new Date()
|
|
61
|
+
const diff = now.getTime() - date.getTime()
|
|
62
|
+
const minutes = Math.floor(diff / 60000)
|
|
63
|
+
const hours = Math.floor(diff / 3600000)
|
|
64
|
+
const days = Math.floor(diff / 86400000)
|
|
65
|
+
|
|
66
|
+
if (minutes < 60) return `${minutes}m ago`
|
|
67
|
+
if (hours < 24) return `${hours}h ago`
|
|
68
|
+
return `${days}d ago`
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const statusConfig = {
|
|
72
|
+
running: {
|
|
73
|
+
color: "text-green-500",
|
|
74
|
+
bgColor: "bg-green-500/10",
|
|
75
|
+
borderColor: "border-green-500/20",
|
|
76
|
+
icon: CheckCircle,
|
|
77
|
+
label: "Running"
|
|
78
|
+
},
|
|
79
|
+
stopped: {
|
|
80
|
+
color: "text-gray-500",
|
|
81
|
+
bgColor: "bg-gray-500/10",
|
|
82
|
+
borderColor: "border-gray-500/20",
|
|
83
|
+
icon: Activity,
|
|
84
|
+
label: "Stopped"
|
|
85
|
+
},
|
|
86
|
+
error: {
|
|
87
|
+
color: "text-red-500",
|
|
88
|
+
bgColor: "bg-red-500/10",
|
|
89
|
+
borderColor: "border-red-500/20",
|
|
90
|
+
icon: AlertTriangle,
|
|
91
|
+
label: "Error"
|
|
92
|
+
},
|
|
93
|
+
restarting: {
|
|
94
|
+
color: "text-yellow-500",
|
|
95
|
+
bgColor: "bg-yellow-500/10",
|
|
96
|
+
borderColor: "border-yellow-500/20",
|
|
97
|
+
icon: Loader2,
|
|
98
|
+
label: "Restarting"
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const freshclamConfig = {
|
|
103
|
+
running: { color: "text-green-500", label: "Running" },
|
|
104
|
+
stopped: { color: "text-gray-500", label: "Stopped" },
|
|
105
|
+
error: { color: "text-red-500", label: "Error" },
|
|
106
|
+
updating: { color: "text-blue-500", label: "Updating" }
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const uptimeStatusConfig = {
|
|
110
|
+
up: "bg-green-500",
|
|
111
|
+
degraded: "bg-yellow-500",
|
|
112
|
+
down: "bg-red-500"
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export const ClamAVServiceStatus = ({
|
|
116
|
+
status = "running",
|
|
117
|
+
version = "1.0.3",
|
|
118
|
+
uptime = 259200, // 3 days
|
|
119
|
+
metrics = {
|
|
120
|
+
cpuUsage: 2.5,
|
|
121
|
+
memoryUsage: 512,
|
|
122
|
+
threads: 4,
|
|
123
|
+
scanQueue: 0,
|
|
124
|
+
activeConnections: 3
|
|
125
|
+
},
|
|
126
|
+
signatures = {
|
|
127
|
+
version: "27456",
|
|
128
|
+
count: 8764532,
|
|
129
|
+
lastUpdate: new Date(Date.now() - 3600000), // 1h ago
|
|
130
|
+
freshclamStatus: "running",
|
|
131
|
+
nextScheduledUpdate: new Date(Date.now() + 3600000) // in 1h
|
|
132
|
+
},
|
|
133
|
+
socketType = "tcp",
|
|
134
|
+
socketPath = "3310",
|
|
135
|
+
uptimeHistory = [],
|
|
136
|
+
onRestart,
|
|
137
|
+
onUpdateSignatures,
|
|
138
|
+
onViewLogs,
|
|
139
|
+
className
|
|
140
|
+
}: ClamAVServiceStatusProps) => {
|
|
141
|
+
const config = statusConfig[status]
|
|
142
|
+
const StatusIcon = config.icon
|
|
143
|
+
|
|
144
|
+
return (
|
|
145
|
+
<div className={cn("space-y-4", className)}>
|
|
146
|
+
{/* Main Status Card */}
|
|
147
|
+
<Card className={cn("p-6 border-2", config.borderColor, config.bgColor)}>
|
|
148
|
+
<div className="flex items-start justify-between gap-4">
|
|
149
|
+
<div className="flex items-start gap-4">
|
|
150
|
+
<div className={cn("p-3 rounded-xl", config.bgColor)}>
|
|
151
|
+
<StatusIcon className={cn("w-8 h-8", config.color, status === "restarting" && "animate-spin")} />
|
|
152
|
+
</div>
|
|
153
|
+
<div className="space-y-1">
|
|
154
|
+
<div className="flex items-center gap-2">
|
|
155
|
+
<h3 className="text-2xl font-semibold">ClamAV Daemon</h3>
|
|
156
|
+
<Badge variant={status === "error" ? "destructive" : "secondary"} className={status === "running" ? "bg-green-500/10 text-green-600 border-green-500/20" : ""}>
|
|
157
|
+
{config.label}
|
|
158
|
+
</Badge>
|
|
159
|
+
</div>
|
|
160
|
+
<div className="flex items-center gap-4 text-sm text-muted-foreground">
|
|
161
|
+
<span>Version {version}</span>
|
|
162
|
+
<span className="flex items-center gap-1">
|
|
163
|
+
<Clock className="w-3.5 h-3.5" />
|
|
164
|
+
Uptime: {formatUptime(uptime)}
|
|
165
|
+
</span>
|
|
166
|
+
</div>
|
|
167
|
+
</div>
|
|
168
|
+
</div>
|
|
169
|
+
|
|
170
|
+
<div className="flex items-center gap-2">
|
|
171
|
+
<Button variant="outline" size="sm" onClick={onViewLogs}>
|
|
172
|
+
<Terminal className="w-4 h-4 mr-2" />
|
|
173
|
+
Logs
|
|
174
|
+
</Button>
|
|
175
|
+
<Button variant="outline" size="sm" onClick={onRestart} disabled={status === "restarting"}>
|
|
176
|
+
<RefreshCw className={cn("w-4 h-4 mr-2", status === "restarting" && "animate-spin")} />
|
|
177
|
+
Restart
|
|
178
|
+
</Button>
|
|
179
|
+
</div>
|
|
180
|
+
</div>
|
|
181
|
+
</Card>
|
|
182
|
+
|
|
183
|
+
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
|
184
|
+
{/* System Metrics */}
|
|
185
|
+
<Card className="p-6">
|
|
186
|
+
<div className="flex items-center gap-2 mb-4">
|
|
187
|
+
<Activity className="w-5 h-5 text-primary" />
|
|
188
|
+
<h4 className="font-semibold">System Metrics</h4>
|
|
189
|
+
</div>
|
|
190
|
+
|
|
191
|
+
<div className="space-y-4">
|
|
192
|
+
<div>
|
|
193
|
+
<div className="flex justify-between text-sm mb-1.5">
|
|
194
|
+
<span className="text-muted-foreground">CPU Usage</span>
|
|
195
|
+
<span className="font-medium">{metrics.cpuUsage.toFixed(1)}%</span>
|
|
196
|
+
</div>
|
|
197
|
+
<Progress
|
|
198
|
+
value={metrics.cpuUsage}
|
|
199
|
+
className={cn(
|
|
200
|
+
"h-2",
|
|
201
|
+
metrics.cpuUsage > 80 && "bg-red-500/20",
|
|
202
|
+
metrics.cpuUsage > 50 && metrics.cpuUsage <= 80 && "bg-yellow-500/20"
|
|
203
|
+
)}
|
|
204
|
+
/>
|
|
205
|
+
</div>
|
|
206
|
+
|
|
207
|
+
<div>
|
|
208
|
+
<div className="flex justify-between text-sm mb-1.5">
|
|
209
|
+
<span className="text-muted-foreground">Memory Usage</span>
|
|
210
|
+
<span className="font-medium">{metrics.memoryUsage} MB</span>
|
|
211
|
+
</div>
|
|
212
|
+
<Progress
|
|
213
|
+
value={(metrics.memoryUsage / 1024) * 100}
|
|
214
|
+
className={cn(
|
|
215
|
+
"h-2",
|
|
216
|
+
metrics.memoryUsage > 800 && "bg-red-500/20",
|
|
217
|
+
metrics.memoryUsage > 600 && metrics.memoryUsage <= 800 && "bg-yellow-500/20"
|
|
218
|
+
)}
|
|
219
|
+
/>
|
|
220
|
+
</div>
|
|
221
|
+
|
|
222
|
+
<div className="grid grid-cols-3 gap-4 pt-2">
|
|
223
|
+
<div className="space-y-1">
|
|
224
|
+
<div className="text-xs text-muted-foreground">Threads</div>
|
|
225
|
+
<div className="text-2xl font-semibold">{metrics.threads}</div>
|
|
226
|
+
</div>
|
|
227
|
+
<div className="space-y-1">
|
|
228
|
+
<div className="text-xs text-muted-foreground">Scan Queue</div>
|
|
229
|
+
<div className="text-2xl font-semibold">{metrics.scanQueue}</div>
|
|
230
|
+
</div>
|
|
231
|
+
<div className="space-y-1">
|
|
232
|
+
<div className="text-xs text-muted-foreground">Connections</div>
|
|
233
|
+
<div className="text-2xl font-semibold flex items-center gap-1">
|
|
234
|
+
{metrics.activeConnections}
|
|
235
|
+
<Wifi className="w-4 h-4 text-muted-foreground" />
|
|
236
|
+
</div>
|
|
237
|
+
</div>
|
|
238
|
+
</div>
|
|
239
|
+
</div>
|
|
240
|
+
</Card>
|
|
241
|
+
|
|
242
|
+
{/* Signature Database */}
|
|
243
|
+
<Card className="p-6">
|
|
244
|
+
<div className="flex items-center justify-between mb-4">
|
|
245
|
+
<div className="flex items-center gap-2">
|
|
246
|
+
<Database className="w-5 h-5 text-primary" />
|
|
247
|
+
<h4 className="font-semibold">Signature Database</h4>
|
|
248
|
+
</div>
|
|
249
|
+
<Button variant="outline" size="sm" onClick={onUpdateSignatures}>
|
|
250
|
+
<RefreshCw className="w-4 h-4 mr-2" />
|
|
251
|
+
Update
|
|
252
|
+
</Button>
|
|
253
|
+
</div>
|
|
254
|
+
|
|
255
|
+
<div className="space-y-4">
|
|
256
|
+
<div className="grid grid-cols-2 gap-4">
|
|
257
|
+
<div className="space-y-1">
|
|
258
|
+
<div className="text-xs text-muted-foreground">Version</div>
|
|
259
|
+
<div className="text-lg font-semibold">{signatures.version}</div>
|
|
260
|
+
</div>
|
|
261
|
+
<div className="space-y-1">
|
|
262
|
+
<div className="text-xs text-muted-foreground">Signatures</div>
|
|
263
|
+
<div className="text-lg font-semibold">{signatures.count.toLocaleString()}</div>
|
|
264
|
+
</div>
|
|
265
|
+
</div>
|
|
266
|
+
|
|
267
|
+
<div className="space-y-2 pt-2 border-t">
|
|
268
|
+
<div className="flex justify-between text-sm">
|
|
269
|
+
<span className="text-muted-foreground">Last Updated</span>
|
|
270
|
+
<span className="font-medium">{formatDate(signatures.lastUpdate)}</span>
|
|
271
|
+
</div>
|
|
272
|
+
{signatures.nextScheduledUpdate && (
|
|
273
|
+
<div className="flex justify-between text-sm">
|
|
274
|
+
<span className="text-muted-foreground">Next Update</span>
|
|
275
|
+
<span className="font-medium">in {formatDate(new Date(Date.now() - (Date.now() - signatures.nextScheduledUpdate.getTime())))}</span>
|
|
276
|
+
</div>
|
|
277
|
+
)}
|
|
278
|
+
</div>
|
|
279
|
+
|
|
280
|
+
<div className="flex items-center justify-between pt-2 border-t">
|
|
281
|
+
<span className="text-sm text-muted-foreground">Freshclam Status</span>
|
|
282
|
+
<Badge
|
|
283
|
+
variant={
|
|
284
|
+
signatures.freshclamStatus === "error" ? "destructive" :
|
|
285
|
+
signatures.freshclamStatus === "updating" ? "default" :
|
|
286
|
+
"secondary"
|
|
287
|
+
}
|
|
288
|
+
className={signatures.freshclamStatus === "running" ? "bg-green-500/10 text-green-600 border-green-500/20" : ""}
|
|
289
|
+
>
|
|
290
|
+
<span className={cn(freshclamConfig[signatures.freshclamStatus].color)}>
|
|
291
|
+
{freshclamConfig[signatures.freshclamStatus].label}
|
|
292
|
+
</span>
|
|
293
|
+
</Badge>
|
|
294
|
+
</div>
|
|
295
|
+
</div>
|
|
296
|
+
</Card>
|
|
297
|
+
</div>
|
|
298
|
+
|
|
299
|
+
{/* Connection Info & Uptime History */}
|
|
300
|
+
<div className="grid grid-cols-1 lg:grid-cols-3 gap-4">
|
|
301
|
+
<Card className="p-6">
|
|
302
|
+
<div className="flex items-center gap-2 mb-4">
|
|
303
|
+
<Wifi className="w-5 h-5 text-primary" />
|
|
304
|
+
<h4 className="font-semibold">Connection</h4>
|
|
305
|
+
</div>
|
|
306
|
+
|
|
307
|
+
<div className="space-y-3">
|
|
308
|
+
<div className="flex justify-between">
|
|
309
|
+
<span className="text-sm text-muted-foreground">Socket Type</span>
|
|
310
|
+
<Badge variant="outline">{socketType.toUpperCase()}</Badge>
|
|
311
|
+
</div>
|
|
312
|
+
<div className="flex justify-between">
|
|
313
|
+
<span className="text-sm text-muted-foreground">{socketType === "tcp" ? "Port" : "Path"}</span>
|
|
314
|
+
<span className="text-sm font-mono font-medium">{socketPath}</span>
|
|
315
|
+
</div>
|
|
316
|
+
<div className="flex justify-between pt-2 border-t">
|
|
317
|
+
<span className="text-sm text-muted-foreground">Active Connections</span>
|
|
318
|
+
<span className="text-lg font-semibold">{metrics.activeConnections}</span>
|
|
319
|
+
</div>
|
|
320
|
+
</div>
|
|
321
|
+
</Card>
|
|
322
|
+
|
|
323
|
+
<Card className="p-6 lg:col-span-2">
|
|
324
|
+
<div className="flex items-center gap-2 mb-4">
|
|
325
|
+
<HardDrive className="w-5 h-5 text-primary" />
|
|
326
|
+
<h4 className="font-semibold">Uptime History (Last 7 Days)</h4>
|
|
327
|
+
</div>
|
|
328
|
+
|
|
329
|
+
{uptimeHistory.length > 0 ? (
|
|
330
|
+
<div className="flex items-end justify-between gap-1 h-20">
|
|
331
|
+
{uptimeHistory.map((entry, index) => (
|
|
332
|
+
<div key={index} className="flex-1 flex flex-col items-center gap-1">
|
|
333
|
+
<div
|
|
334
|
+
className={cn(
|
|
335
|
+
"w-full rounded-t transition-all",
|
|
336
|
+
uptimeStatusConfig[entry.status],
|
|
337
|
+
entry.status === "up" && "h-full",
|
|
338
|
+
entry.status === "degraded" && "h-3/4",
|
|
339
|
+
entry.status === "down" && "h-1/4"
|
|
340
|
+
)}
|
|
341
|
+
/>
|
|
342
|
+
<span className="text-[10px] text-muted-foreground">{entry.hour}</span>
|
|
343
|
+
</div>
|
|
344
|
+
))}
|
|
345
|
+
</div>
|
|
346
|
+
) : (
|
|
347
|
+
<div className="h-20 flex items-center justify-center text-sm text-muted-foreground">
|
|
348
|
+
No uptime history available
|
|
349
|
+
</div>
|
|
350
|
+
)}
|
|
351
|
+
|
|
352
|
+
<div className="flex items-center justify-center gap-4 mt-4 pt-4 border-t">
|
|
353
|
+
<div className="flex items-center gap-1.5">
|
|
354
|
+
<div className="w-3 h-3 rounded bg-green-500" />
|
|
355
|
+
<span className="text-xs text-muted-foreground">Up</span>
|
|
356
|
+
</div>
|
|
357
|
+
<div className="flex items-center gap-1.5">
|
|
358
|
+
<div className="w-3 h-3 rounded bg-yellow-500" />
|
|
359
|
+
<span className="text-xs text-muted-foreground">Degraded</span>
|
|
360
|
+
</div>
|
|
361
|
+
<div className="flex items-center gap-1.5">
|
|
362
|
+
<div className="w-3 h-3 rounded bg-red-500" />
|
|
363
|
+
<span className="text-xs text-muted-foreground">Down</span>
|
|
364
|
+
</div>
|
|
365
|
+
</div>
|
|
366
|
+
</Card>
|
|
367
|
+
</div>
|
|
368
|
+
</div>
|
|
369
|
+
)
|
|
370
|
+
}
|