feat: implement service process management, event logging, and monitoring UI with i18n support

This commit is contained in:
Ümit Tunç
2026-04-02 14:55:21 +03:00
parent b28c088558
commit bf24818b83
10 changed files with 1248 additions and 27 deletions
+764
View File
@@ -15,6 +15,8 @@
"@emotion/styled": "^11.11.0", "@emotion/styled": "^11.11.0",
"@mui/icons-material": "^6.0.0", "@mui/icons-material": "^6.0.0",
"@mui/material": "^6.0.0", "@mui/material": "^6.0.0",
"@mui/x-charts": "^8.28.2",
"@mui/x-data-grid": "^8.28.2",
"@types/pidusage": "^2.0.5", "@types/pidusage": "^2.0.5",
"@types/prismjs": "^1.26.6", "@types/prismjs": "^1.26.6",
"axios": "^1.14.0", "axios": "^1.14.0",
@@ -26,6 +28,7 @@
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-i18next": "^13.0.0", "react-i18next": "^13.0.0",
"react-simple-code-editor": "^0.14.1", "react-simple-code-editor": "^0.14.1",
"recharts": "^3.8.1",
"sweetalert2": "^11.26.24" "sweetalert2": "^11.26.24"
}, },
"devDependencies": { "devDependencies": {
@@ -1838,6 +1841,353 @@
} }
} }
}, },
"node_modules/@mui/x-charts": {
"version": "8.28.2",
"resolved": "https://registry.npmjs.org/@mui/x-charts/-/x-charts-8.28.2.tgz",
"integrity": "sha512-xvQto+uVcwwWitZjzwmHaw1KZDsIju687kLSyOe3qsg4JYldgT+WWguBFQREF7Tsw7PFGAPahDAzbsTNuIkkTA==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.28.4",
"@mui/utils": "^7.3.5",
"@mui/x-charts-vendor": "8.26.0",
"@mui/x-internal-gestures": "0.4.0",
"@mui/x-internals": "8.26.0",
"bezier-easing": "^2.1.0",
"clsx": "^2.1.1",
"prop-types": "^15.8.1",
"reselect": "^5.1.1",
"use-sync-external-store": "^1.6.0"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"@emotion/react": "^11.9.0",
"@emotion/styled": "^11.8.1",
"@mui/material": "^5.15.14 || ^6.0.0 || ^7.0.0",
"@mui/system": "^5.15.14 || ^6.0.0 || ^7.0.0",
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@emotion/react": {
"optional": true
},
"@emotion/styled": {
"optional": true
}
}
},
"node_modules/@mui/x-charts-vendor": {
"version": "8.26.0",
"resolved": "https://registry.npmjs.org/@mui/x-charts-vendor/-/x-charts-vendor-8.26.0.tgz",
"integrity": "sha512-R//+WSWvsLJRTjTRN90EKX9sgRzAb4HQBvtUA3cTQpkGrmEjmatD4BJAm3IdRdkSagf6yKWF+ypESctyRhbwnA==",
"license": "MIT AND ISC",
"dependencies": {
"@babel/runtime": "^7.28.4",
"@types/d3-array": "^3.2.2",
"@types/d3-color": "^3.1.3",
"@types/d3-format": "^3.0.4",
"@types/d3-interpolate": "^3.0.4",
"@types/d3-path": "^3.1.1",
"@types/d3-scale": "^4.0.9",
"@types/d3-shape": "^3.1.7",
"@types/d3-time": "^3.0.4",
"@types/d3-time-format": "^4.0.3",
"@types/d3-timer": "^3.0.2",
"d3-array": "^3.2.4",
"d3-color": "^3.1.0",
"d3-format": "^3.1.0",
"d3-interpolate": "^3.0.1",
"d3-path": "^3.1.0",
"d3-scale": "^4.0.2",
"d3-shape": "^3.2.0",
"d3-time": "^3.1.0",
"d3-time-format": "^4.1.0",
"d3-timer": "^3.0.1",
"flatqueue": "^3.0.0",
"internmap": "^2.0.3"
}
},
"node_modules/@mui/x-charts/node_modules/@mui/types": {
"version": "7.4.12",
"resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.12.tgz",
"integrity": "sha512-iKNAF2u9PzSIj40CjvKJWxFXJo122jXVdrmdh0hMYd+FR+NuJMkr/L88XwWLCRiJ5P1j+uyac25+Kp6YC4hu6w==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.28.6"
},
"peerDependencies": {
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@mui/x-charts/node_modules/@mui/utils": {
"version": "7.3.9",
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.3.9.tgz",
"integrity": "sha512-U6SdZaGbfb65fqTsH3V5oJdFj9uYwyLE2WVuNvmbggTSDBb8QHrFsqY8BN3taK9t3yJ8/BPHD/kNvLNyjwM7Yw==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.28.6",
"@mui/types": "^7.4.12",
"@types/prop-types": "^15.7.15",
"clsx": "^2.1.1",
"prop-types": "^15.8.1",
"react-is": "^19.2.3"
},
"engines": {
"node": ">=14.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mui-org"
},
"peerDependencies": {
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@mui/x-data-grid": {
"version": "8.28.2",
"resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-8.28.2.tgz",
"integrity": "sha512-nqVhOtXOfPcKtoULuZuUp9Hdgzcn4xzqjYwqxZkTPnzKGZqkXPGaupy6n+9ssyYg7jg9ot+zNUJB0sbsvlDY/g==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.28.4",
"@mui/utils": "^7.3.5",
"@mui/x-internals": "8.26.0",
"@mui/x-virtualizer": "0.3.4",
"clsx": "^2.1.1",
"prop-types": "^15.8.1",
"use-sync-external-store": "^1.6.0"
},
"engines": {
"node": ">=14.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mui-org"
},
"peerDependencies": {
"@emotion/react": "^11.9.0",
"@emotion/styled": "^11.8.1",
"@mui/material": "^5.15.14 || ^6.0.0 || ^7.0.0",
"@mui/system": "^5.15.14 || ^6.0.0 || ^7.0.0",
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@emotion/react": {
"optional": true
},
"@emotion/styled": {
"optional": true
}
}
},
"node_modules/@mui/x-data-grid/node_modules/@mui/types": {
"version": "7.4.12",
"resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.12.tgz",
"integrity": "sha512-iKNAF2u9PzSIj40CjvKJWxFXJo122jXVdrmdh0hMYd+FR+NuJMkr/L88XwWLCRiJ5P1j+uyac25+Kp6YC4hu6w==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.28.6"
},
"peerDependencies": {
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@mui/x-data-grid/node_modules/@mui/utils": {
"version": "7.3.9",
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.3.9.tgz",
"integrity": "sha512-U6SdZaGbfb65fqTsH3V5oJdFj9uYwyLE2WVuNvmbggTSDBb8QHrFsqY8BN3taK9t3yJ8/BPHD/kNvLNyjwM7Yw==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.28.6",
"@mui/types": "^7.4.12",
"@types/prop-types": "^15.7.15",
"clsx": "^2.1.1",
"prop-types": "^15.8.1",
"react-is": "^19.2.3"
},
"engines": {
"node": ">=14.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mui-org"
},
"peerDependencies": {
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@mui/x-internal-gestures": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/@mui/x-internal-gestures/-/x-internal-gestures-0.4.0.tgz",
"integrity": "sha512-i0W6v9LoiNY8Yf1goOmaygtz/ncPJGBedhpDfvNg/i8BvzPwJcBaeW4rqPucJfVag9KQ8MSssBBrvYeEnrQmhw==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.28.4"
}
},
"node_modules/@mui/x-internals": {
"version": "8.26.0",
"resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-8.26.0.tgz",
"integrity": "sha512-B9OZau5IQUvIxwpJZhoFJKqRpmWf5r0yMmSXjQuqb5WuqM755EuzWJOenY48denGoENzMLT8hQpA0hRTeU2IPA==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.28.4",
"@mui/utils": "^7.3.5",
"reselect": "^5.1.1",
"use-sync-external-store": "^1.6.0"
},
"engines": {
"node": ">=14.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mui-org"
},
"peerDependencies": {
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/@mui/x-internals/node_modules/@mui/types": {
"version": "7.4.12",
"resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.12.tgz",
"integrity": "sha512-iKNAF2u9PzSIj40CjvKJWxFXJo122jXVdrmdh0hMYd+FR+NuJMkr/L88XwWLCRiJ5P1j+uyac25+Kp6YC4hu6w==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.28.6"
},
"peerDependencies": {
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@mui/x-internals/node_modules/@mui/utils": {
"version": "7.3.9",
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.3.9.tgz",
"integrity": "sha512-U6SdZaGbfb65fqTsH3V5oJdFj9uYwyLE2WVuNvmbggTSDBb8QHrFsqY8BN3taK9t3yJ8/BPHD/kNvLNyjwM7Yw==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.28.6",
"@mui/types": "^7.4.12",
"@types/prop-types": "^15.7.15",
"clsx": "^2.1.1",
"prop-types": "^15.8.1",
"react-is": "^19.2.3"
},
"engines": {
"node": ">=14.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mui-org"
},
"peerDependencies": {
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@mui/x-virtualizer": {
"version": "0.3.4",
"resolved": "https://registry.npmjs.org/@mui/x-virtualizer/-/x-virtualizer-0.3.4.tgz",
"integrity": "sha512-b80Wt08bT+KVDunurdd4es76PdYaazoNGpGde3Ki7jGVaSpoljMYSRVlCtifei+4cOUD3NKJhG4e3av/czTekg==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.28.4",
"@mui/utils": "^7.3.5",
"@mui/x-internals": "8.26.0"
},
"engines": {
"node": ">=14.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mui-org"
},
"peerDependencies": {
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/@mui/x-virtualizer/node_modules/@mui/types": {
"version": "7.4.12",
"resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.12.tgz",
"integrity": "sha512-iKNAF2u9PzSIj40CjvKJWxFXJo122jXVdrmdh0hMYd+FR+NuJMkr/L88XwWLCRiJ5P1j+uyac25+Kp6YC4hu6w==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.28.6"
},
"peerDependencies": {
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@mui/x-virtualizer/node_modules/@mui/utils": {
"version": "7.3.9",
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.3.9.tgz",
"integrity": "sha512-U6SdZaGbfb65fqTsH3V5oJdFj9uYwyLE2WVuNvmbggTSDBb8QHrFsqY8BN3taK9t3yJ8/BPHD/kNvLNyjwM7Yw==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.28.6",
"@mui/types": "^7.4.12",
"@types/prop-types": "^15.7.15",
"clsx": "^2.1.1",
"prop-types": "^15.8.1",
"react-is": "^19.2.3"
},
"engines": {
"node": ">=14.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mui-org"
},
"peerDependencies": {
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@nodelib/fs.scandir": { "node_modules/@nodelib/fs.scandir": {
"version": "2.1.5", "version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -1910,6 +2260,42 @@
"url": "https://opencollective.com/popperjs" "url": "https://opencollective.com/popperjs"
} }
}, },
"node_modules/@reduxjs/toolkit": {
"version": "2.11.2",
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.11.2.tgz",
"integrity": "sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ==",
"license": "MIT",
"dependencies": {
"@standard-schema/spec": "^1.0.0",
"@standard-schema/utils": "^0.3.0",
"immer": "^11.0.0",
"redux": "^5.0.1",
"redux-thunk": "^3.1.0",
"reselect": "^5.1.0"
},
"peerDependencies": {
"react": "^16.9.0 || ^17.0.0 || ^18 || ^19",
"react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0"
},
"peerDependenciesMeta": {
"react": {
"optional": true
},
"react-redux": {
"optional": true
}
}
},
"node_modules/@reduxjs/toolkit/node_modules/immer": {
"version": "11.1.4",
"resolved": "https://registry.npmjs.org/immer/-/immer-11.1.4.tgz",
"integrity": "sha512-XREFCPo6ksxVzP4E0ekD5aMdf8WMwmdNaz6vuvxgI40UaEiu6q3p8X52aU6GdyvLY3XXX/8R7JOTXStz/nBbRw==",
"license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/immer"
}
},
"node_modules/@rolldown/pluginutils": { "node_modules/@rolldown/pluginutils": {
"version": "1.0.0-beta.27", "version": "1.0.0-beta.27",
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz",
@@ -2318,6 +2704,18 @@
"url": "https://github.com/sindresorhus/is?sponsor=1" "url": "https://github.com/sindresorhus/is?sponsor=1"
} }
}, },
"node_modules/@standard-schema/spec": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
"integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
"license": "MIT"
},
"node_modules/@standard-schema/utils": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz",
"integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==",
"license": "MIT"
},
"node_modules/@szmarczak/http-timer": { "node_modules/@szmarczak/http-timer": {
"version": "4.0.6", "version": "4.0.6",
"resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz",
@@ -2397,6 +2795,81 @@
"@types/responselike": "^1.0.0" "@types/responselike": "^1.0.0"
} }
}, },
"node_modules/@types/d3-array": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz",
"integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==",
"license": "MIT"
},
"node_modules/@types/d3-color": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
"integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==",
"license": "MIT"
},
"node_modules/@types/d3-ease": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz",
"integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==",
"license": "MIT"
},
"node_modules/@types/d3-format": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz",
"integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==",
"license": "MIT"
},
"node_modules/@types/d3-interpolate": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
"integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
"license": "MIT",
"dependencies": {
"@types/d3-color": "*"
}
},
"node_modules/@types/d3-path": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz",
"integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==",
"license": "MIT"
},
"node_modules/@types/d3-scale": {
"version": "4.0.9",
"resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz",
"integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==",
"license": "MIT",
"dependencies": {
"@types/d3-time": "*"
}
},
"node_modules/@types/d3-shape": {
"version": "3.1.8",
"resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz",
"integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==",
"license": "MIT",
"dependencies": {
"@types/d3-path": "*"
}
},
"node_modules/@types/d3-time": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz",
"integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==",
"license": "MIT"
},
"node_modules/@types/d3-time-format": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz",
"integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==",
"license": "MIT"
},
"node_modules/@types/d3-timer": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz",
"integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==",
"license": "MIT"
},
"node_modules/@types/debug": { "node_modules/@types/debug": {
"version": "4.1.13", "version": "4.1.13",
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz",
@@ -2539,6 +3012,12 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"node_modules/@types/use-sync-external-store": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz",
"integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==",
"license": "MIT"
},
"node_modules/@types/verror": { "node_modules/@types/verror": {
"version": "1.10.11", "version": "1.10.11",
"resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.11.tgz", "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.11.tgz",
@@ -3184,6 +3663,12 @@
"node": ">=6.0.0" "node": ">=6.0.0"
} }
}, },
"node_modules/bezier-easing": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/bezier-easing/-/bezier-easing-2.1.0.tgz",
"integrity": "sha512-gbIqZ/eslnUFC1tjEvtz0sgx+xTK20wDnYMIA27VA04R7w6xxXQPZDbibjA9DTWZRA2CXtwHykkVzlCaAJAZig==",
"license": "MIT"
},
"node_modules/binary-extensions": { "node_modules/binary-extensions": {
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
@@ -3924,6 +4409,127 @@
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/d3-array": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
"integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
"license": "ISC",
"dependencies": {
"internmap": "1 - 2"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-color": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
"integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-ease": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
"integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-format": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz",
"integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-interpolate": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
"integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
"license": "ISC",
"dependencies": {
"d3-color": "1 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-path": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
"integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-scale": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
"integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
"license": "ISC",
"dependencies": {
"d3-array": "2.10.0 - 3",
"d3-format": "1 - 3",
"d3-interpolate": "1.2.0 - 3",
"d3-time": "2.1.1 - 3",
"d3-time-format": "2 - 4"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-shape": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
"integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
"license": "ISC",
"dependencies": {
"d3-path": "^3.1.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-time": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
"integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
"license": "ISC",
"dependencies": {
"d3-array": "2 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-time-format": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
"integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
"license": "ISC",
"dependencies": {
"d3-time": "1 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-timer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
"integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/data-view-buffer": { "node_modules/data-view-buffer": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz",
@@ -3995,6 +4601,12 @@
} }
} }
}, },
"node_modules/decimal.js-light": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz",
"integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==",
"license": "MIT"
},
"node_modules/decompress": { "node_modules/decompress": {
"version": "4.2.1", "version": "4.2.1",
"resolved": "https://registry.npmjs.org/decompress/-/decompress-4.2.1.tgz", "resolved": "https://registry.npmjs.org/decompress/-/decompress-4.2.1.tgz",
@@ -4911,6 +5523,16 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/es-toolkit": {
"version": "1.45.1",
"resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.45.1.tgz",
"integrity": "sha512-/jhoOj/Fx+A+IIyDNOvO3TItGmlMKhtX8ISAHKE90c4b/k1tqaqEZ+uUqfpU8DMnW5cgNJv606zS55jGvza0Xw==",
"license": "MIT",
"workspaces": [
"docs",
"benchmarks"
]
},
"node_modules/es6-error": { "node_modules/es6-error": {
"version": "4.1.1", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz",
@@ -5292,6 +5914,12 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/eventemitter3": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz",
"integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==",
"license": "MIT"
},
"node_modules/extract-zip": { "node_modules/extract-zip": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
@@ -5453,6 +6081,12 @@
"node": "^10.12.0 || >=12.0.0" "node": "^10.12.0 || >=12.0.0"
} }
}, },
"node_modules/flatqueue": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/flatqueue/-/flatqueue-3.0.0.tgz",
"integrity": "sha512-y1deYaVt+lIc/d2uIcWDNd0CrdQTO5xoCjeFdhX0kSXvm2Acm0o+3bAOiYklTEoRyzwio3sv3/IiBZdusbAe2Q==",
"license": "ISC"
},
"node_modules/flatted": { "node_modules/flatted": {
"version": "3.4.2", "version": "3.4.2",
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz",
@@ -6195,6 +6829,16 @@
"node": ">= 4" "node": ">= 4"
} }
}, },
"node_modules/immer": {
"version": "10.2.0",
"resolved": "https://registry.npmjs.org/immer/-/immer-10.2.0.tgz",
"integrity": "sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw==",
"license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/immer"
}
},
"node_modules/import-fresh": { "node_modules/import-fresh": {
"version": "3.3.1", "version": "3.3.1",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
@@ -6254,6 +6898,15 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/internmap": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
"integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/is-array-buffer": { "node_modules/is-array-buffer": {
"version": "3.0.5", "version": "3.0.5",
"resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
@@ -7977,6 +8630,29 @@
"integrity": "sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA==", "integrity": "sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/react-redux": {
"version": "9.2.0",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
"integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==",
"license": "MIT",
"dependencies": {
"@types/use-sync-external-store": "^0.0.6",
"use-sync-external-store": "^1.4.0"
},
"peerDependencies": {
"@types/react": "^18.2.25 || ^19",
"react": "^18.0 || ^19",
"redux": "^5.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"redux": {
"optional": true
}
}
},
"node_modules/react-refresh": { "node_modules/react-refresh": {
"version": "0.17.0", "version": "0.17.0",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
@@ -8071,6 +8747,51 @@
"node": ">=8.10.0" "node": ">=8.10.0"
} }
}, },
"node_modules/recharts": {
"version": "3.8.1",
"resolved": "https://registry.npmjs.org/recharts/-/recharts-3.8.1.tgz",
"integrity": "sha512-mwzmO1s9sFL0TduUpwndxCUNoXsBw3u3E/0+A+cLcrSfQitSG62L32N69GhqUrrT5qKcAE3pCGVINC6pqkBBQg==",
"license": "MIT",
"workspaces": [
"www"
],
"dependencies": {
"@reduxjs/toolkit": "^1.9.0 || 2.x.x",
"clsx": "^2.1.1",
"decimal.js-light": "^2.5.1",
"es-toolkit": "^1.39.3",
"eventemitter3": "^5.0.1",
"immer": "^10.1.1",
"react-redux": "8.x.x || 9.x.x",
"reselect": "5.1.1",
"tiny-invariant": "^1.3.3",
"use-sync-external-store": "^1.2.2",
"victory-vendor": "^37.0.2"
},
"engines": {
"node": ">=18"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
"react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
"react-is": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/redux": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
"integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==",
"license": "MIT"
},
"node_modules/redux-thunk": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz",
"integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==",
"license": "MIT",
"peerDependencies": {
"redux": "^5.0.0"
}
},
"node_modules/reflect.getprototypeof": { "node_modules/reflect.getprototypeof": {
"version": "1.0.10", "version": "1.0.10",
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
@@ -8125,6 +8846,12 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/reselect": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz",
"integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==",
"license": "MIT"
},
"node_modules/resolve": { "node_modules/resolve": {
"version": "1.22.11", "version": "1.22.11",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",
@@ -9121,6 +9848,12 @@
"integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/tiny-invariant": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
"integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==",
"license": "MIT"
},
"node_modules/tmp": { "node_modules/tmp": {
"version": "0.2.5", "version": "0.2.5",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz",
@@ -9380,6 +10113,15 @@
"punycode": "^2.1.0" "punycode": "^2.1.0"
} }
}, },
"node_modules/use-sync-external-store": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz",
"integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==",
"license": "MIT",
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/utf8-byte-length": { "node_modules/utf8-byte-length": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz", "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz",
@@ -9409,6 +10151,28 @@
"node": ">=0.6.0" "node": ">=0.6.0"
} }
}, },
"node_modules/victory-vendor": {
"version": "37.3.6",
"resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-37.3.6.tgz",
"integrity": "sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==",
"license": "MIT AND ISC",
"dependencies": {
"@types/d3-array": "^3.0.3",
"@types/d3-ease": "^3.0.0",
"@types/d3-interpolate": "^3.0.1",
"@types/d3-scale": "^4.0.2",
"@types/d3-shape": "^3.1.0",
"@types/d3-time": "^3.0.0",
"@types/d3-timer": "^3.0.0",
"d3-array": "^3.1.6",
"d3-ease": "^3.0.1",
"d3-interpolate": "^3.0.1",
"d3-scale": "^4.0.2",
"d3-shape": "^3.1.0",
"d3-time": "^3.0.0",
"d3-timer": "^3.0.1"
}
},
"node_modules/vite": { "node_modules/vite": {
"version": "5.4.21", "version": "5.4.21",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz",
+3
View File
@@ -27,6 +27,8 @@
"@emotion/styled": "^11.11.0", "@emotion/styled": "^11.11.0",
"@mui/icons-material": "^6.0.0", "@mui/icons-material": "^6.0.0",
"@mui/material": "^6.0.0", "@mui/material": "^6.0.0",
"@mui/x-charts": "^8.28.2",
"@mui/x-data-grid": "^8.28.2",
"@types/pidusage": "^2.0.5", "@types/pidusage": "^2.0.5",
"@types/prismjs": "^1.26.6", "@types/prismjs": "^1.26.6",
"axios": "^1.14.0", "axios": "^1.14.0",
@@ -38,6 +40,7 @@
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-i18next": "^13.0.0", "react-i18next": "^13.0.0",
"react-simple-code-editor": "^0.14.1", "react-simple-code-editor": "^0.14.1",
"recharts": "^3.8.1",
"sweetalert2": "^11.26.24" "sweetalert2": "^11.26.24"
}, },
"devDependencies": { "devDependencies": {
+95
View File
@@ -0,0 +1,95 @@
[
{
"service": "mariadb:10.11.8",
"action": "start",
"status": "success",
"id": "4evpwn0ju",
"timestamp": 1775130158656
},
{
"service": "mariadb:10.11.8",
"action": "exit",
"status": "failure",
"details": "Exit code: null",
"id": "9qdrb8lje",
"timestamp": 1775130078423
},
{
"service": "mariadb:10.11.8",
"action": "stop",
"status": "info",
"id": "20l26ok4a",
"timestamp": 1775130078407
},
{
"service": "mariadb:11.4.2",
"action": "start",
"status": "success",
"id": "yi01pzl6i",
"timestamp": 1775130047940
},
{
"service": "mariadb:11.4.2",
"action": "exit",
"status": "failure",
"details": "Exit code: 1",
"id": "kywi5m5pc",
"timestamp": 1775130047898
},
{
"service": "php:5.6.40",
"action": "start",
"status": "success",
"id": "54rhx8r9q",
"timestamp": 1775130043224
},
{
"service": "php:7.4.33",
"action": "start",
"status": "success",
"id": "rwf7gpo22",
"timestamp": 1775130043205
},
{
"service": "php:8.2.30",
"action": "start",
"status": "success",
"id": "v0ilik7d8",
"timestamp": 1775130043185
},
{
"service": "php:8.5.4",
"action": "start",
"status": "success",
"id": "981vuhhw7",
"timestamp": 1775130043168
},
{
"service": "mariadb:10.11.8",
"action": "start",
"status": "success",
"id": "rwx1q9oet",
"timestamp": 1775130042752
},
{
"service": "mariadb:11.4.2",
"action": "start",
"status": "success",
"id": "wh5q33p48",
"timestamp": 1775130042733
},
{
"service": "apache",
"action": "start",
"status": "success",
"id": "6tbgu0wb4",
"timestamp": 1775130042184
},
{
"service": "nginx",
"action": "start",
"status": "success",
"id": "mrd4fgm4i",
"timestamp": 1775130042115
}
]
+3
View File
@@ -10,6 +10,9 @@
"cliEnvEnabled": true, "cliEnvEnabled": true,
"defaultPhpVersion": "php-7.4.33", "defaultPhpVersion": "php-7.4.33",
"defaultMariaDbVersion": "mariadb-11.4.2", "defaultMariaDbVersion": "mariadb-11.4.2",
"mariaDbTuning": {
"mariadb-10.11.8": "\nmax_connections=2720\ninnodb_buffer_pool_size=8960M\nkey_buffer_size=800M\nmax_allowed_packet=1024M"
},
"mysqlPort": "3306", "mysqlPort": "3306",
"mysqlPorts": {} "mysqlPorts": {}
} }
+10 -24
View File
@@ -14,6 +14,7 @@ import path from 'path'
import { mariaDbOperationService } from '../services/MariaDbOperationService' import { mariaDbOperationService } from '../services/MariaDbOperationService'
import { cliAliasService } from '../services/CliAliasService' import { cliAliasService } from '../services/CliAliasService'
import { cliBinService } from '../services/CliBinService' import { cliBinService } from '../services/CliBinService'
import { serviceEventLogger } from '../services/ServiceEventLogger'
function findExecutable(dir: string, name: string): string | null { function findExecutable(dir: string, name: string): string | null {
if (!fs.existsSync(dir)) return null if (!fs.existsSync(dir)) return null
@@ -311,6 +312,15 @@ export function registerIpcHandlers(): void {
return { success: false, message: 'Bu servis için reload desteklenmiyor.' } return { success: false, message: 'Bu servis için reload desteklenmiyor.' }
}) })
ipcMain.handle('services:get-events', async () => {
return serviceEventLogger.getEvents()
})
ipcMain.handle('services:clear-events', async () => {
serviceEventLogger.clear()
return { success: true }
})
ipcMain.handle('services:start', async (_event, serviceName: string) => { ipcMain.handle('services:start', async (_event, serviceName: string) => {
try { try {
const settings = configService.getSettings() const settings = configService.getSettings()
@@ -576,30 +586,6 @@ export function registerIpcHandlers(): void {
return result return result
}) })
ipcMain.handle('php:config:get', async (_event, version: string) => {
const phpDir = path.join(configService.getBasePath(), 'bin', `php-${version}`)
const iniPath = path.join(phpDir, 'php.ini')
if (fs.existsSync(iniPath)) {
return {
content: fs.readFileSync(iniPath, 'utf8'),
path: iniPath
}
}
return { content: '', path: iniPath }
})
ipcMain.handle('php:config:save', async (_event, { version, content }) => {
const phpDir = path.join(configService.getBasePath(), 'bin', `php-${version}`)
const iniPath = path.join(phpDir, 'php.ini')
fs.writeFileSync(iniPath, content)
const serviceName = `php:${version}`
if (processManager.isServiceRunning(serviceName)) {
await processManager.stopService(serviceName)
}
return { success: true, message: 'Configuration saved.' }
})
// MariaDB Versions // MariaDB Versions
ipcMain.handle('mariadb:versions', async () => { ipcMain.handle('mariadb:versions', async () => {
+7
View File
@@ -4,6 +4,7 @@ import { app } from 'electron'
import fs from 'fs' import fs from 'fs'
import pidusage from 'pidusage' import pidusage from 'pidusage'
import { configService } from './ConfigService' import { configService } from './ConfigService'
import { serviceEventLogger } from './ServiceEventLogger'
import { t } from '../utils/i18n' import { t } from '../utils/i18n'
export class ProcessManager { export class ProcessManager {
@@ -130,17 +131,21 @@ export class ProcessManager {
console.log(`[PROCESS] Started ${name} with PID ${child.pid}`) console.log(`[PROCESS] Started ${name} with PID ${child.pid}`)
this.addLog(name, `${t('common.running')} (PID: ${child.pid})`) this.addLog(name, `${t('common.running')} (PID: ${child.pid})`)
serviceEventLogger.log({ service: name, action: 'start', status: 'success' })
child.stdout?.on('data', (data) => this.addLog(name, data.toString())) child.stdout?.on('data', (data) => this.addLog(name, data.toString()))
child.stderr?.on('data', (data) => this.addLog(name, data.toString())) child.stderr?.on('data', (data) => this.addLog(name, data.toString()))
child.on('error', (err) => { child.on('error', (err) => {
this.addLog(name, `Failed to start: ${err.message}`) this.addLog(name, `Failed to start: ${err.message}`)
serviceEventLogger.log({ service: name, action: 'start', status: 'failure', details: err.message })
this.processes.delete(name) this.processes.delete(name)
}) })
child.on('exit', (code) => { child.on('exit', (code) => {
const status = code === 0 ? 'success' : 'failure'
this.addLog(name, `${t('common.stopped')} (Code: ${code})`) this.addLog(name, `${t('common.stopped')} (Code: ${code})`)
serviceEventLogger.log({ service: name, action: 'exit', status, details: `Exit code: ${code}` })
if (code !== 0) { if (code !== 0) {
this.checkErrorLogs(name) this.checkErrorLogs(name)
} }
@@ -152,6 +157,7 @@ export class ProcessManager {
return true return true
} catch (error: any) { } catch (error: any) {
this.addLog(name, `Spawn Error: ${error.message}`) this.addLog(name, `Spawn Error: ${error.message}`)
serviceEventLogger.log({ service: name, action: 'start', status: 'failure', details: error.message })
return false return false
} }
} }
@@ -160,6 +166,7 @@ export class ProcessManager {
const child = this.processes.get(name) const child = this.processes.get(name)
if (child) { if (child) {
this.addLog(name, `${t('common.stopping')}...`) this.addLog(name, `${t('common.stopping')}...`)
serviceEventLogger.log({ service: name, action: 'stop', status: 'info' })
try { try {
if (process.platform === 'win32') { if (process.platform === 'win32') {
child.kill() child.kill()
+68
View File
@@ -0,0 +1,68 @@
import fs from 'fs'
import path from 'path'
import { configService } from './ConfigService'
export interface ServiceEvent {
id: string
service: string
action: 'start' | 'stop' | 'exit' | 'error' | 'restart'
timestamp: number
details?: string
status: 'success' | 'failure' | 'info'
}
export class ServiceEventLogger {
private filePath: string
private events: ServiceEvent[] = []
private maxEvents = 1000
constructor() {
this.filePath = path.join(configService.getBasePath(), 'service_events.json')
this.load()
}
private load() {
try {
if (fs.existsSync(this.filePath)) {
const content = fs.readFileSync(this.filePath, 'utf8')
this.events = JSON.parse(content)
}
} catch (e) {
console.error('Failed to load service events:', e)
this.events = []
}
}
private save() {
try {
// Keep only the last N events
if (this.events.length > this.maxEvents) {
this.events = this.events.slice(-this.maxEvents)
}
fs.writeFileSync(this.filePath, JSON.stringify(this.events, null, 2))
} catch (e) {
console.error('Failed to save service events:', e)
}
}
log(event: Omit<ServiceEvent, 'id' | 'timestamp'>) {
const newEvent: ServiceEvent = {
...event,
id: Math.random().toString(36).substr(2, 9),
timestamp: Date.now()
}
this.events.unshift(newEvent) // Add to top
this.save()
}
getEvents() {
return this.events
}
clear() {
this.events = []
this.save()
}
}
export const serviceEventLogger = new ServiceEventLogger()
@@ -0,0 +1,261 @@
import { useState, useEffect, useMemo } from 'react'
import {
Box,
Typography,
IconButton,
Tooltip,
Stack,
Button,
Paper,
Divider,
Alert,
CircularProgress,
Chip
} from '@mui/material'
import {
Refresh as RefreshIcon,
DeleteSweep as ClearIcon,
Close as CloseIcon,
History as HistoryIcon,
CheckCircle as SuccessIcon,
Error as ErrorIcon,
Info as InfoIcon,
PlayArrow as StartIcon,
Stop as StopIcon
} from '@mui/icons-material'
import { DataGrid, GridColDef } from '@mui/x-data-grid'
import { BarChart } from '@mui/x-charts'
import { useTranslation } from 'react-i18next'
import Swal from 'sweetalert2'
interface ServiceEvent {
id: string
service: string
action: 'start' | 'stop' | 'exit' | 'error' | 'restart'
timestamp: number
details?: string
status: 'success' | 'failure' | 'info'
}
export default function ServiceEventMonitor({ onClose }: { onClose?: () => void }) {
const { t } = useTranslation()
const [events, setEvents] = useState<ServiceEvent[]>([])
const [loading, setLoading] = useState(true)
const fetchEvents = async () => {
if (!window.api) return
setLoading(true)
try {
const data = await window.api.invoke('services:get-events')
setEvents(data)
} catch (error) {
console.error('Failed to fetch events:', error)
}
setLoading(false)
}
useEffect(() => {
fetchEvents()
const interval = setInterval(fetchEvents, 5000)
return () => clearInterval(interval)
}, [])
const handleClear = async () => {
const result = await Swal.fire({
title: t('service_monitor.clear_confirm'),
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#d33',
confirmButtonText: t('common.yes_delete'),
cancelButtonText: t('common.cancel'),
background: '#1a1d21',
color: '#fff'
})
if (result.isConfirmed && window.api) {
await window.api.invoke('services:clear-events')
fetchEvents()
}
}
// Prepare chart data
const chartData = useMemo(() => {
const services = Array.from(new Set(events.map(e => e.service)))
const data = services.map(s => {
const serviceEvents = events.filter(e => e.service === s)
return {
service: s,
start: serviceEvents.filter(e => e.action === 'start' && e.status === 'success').length,
stop: serviceEvents.filter(e => e.action === 'stop' || e.action === 'exit').length,
error: serviceEvents.filter(e => e.status === 'failure' || e.action === 'error').length
}
})
return data
}, [events])
const columns: GridColDef[] = [
{
field: 'timestamp',
headerName: t('service_monitor.columns.timestamp'),
width: 130,
valueGetter: (params) => new Date(params).toLocaleTimeString()
},
{
field: 'service',
headerName: t('service_monitor.columns.service'),
width: 120,
renderCell: (params) => (
<Typography variant="body2" sx={{ fontWeight: 'bold' }}>
{params.value}
</Typography>
)
},
{
field: 'action',
headerName: t('service_monitor.columns.action'),
width: 100,
renderCell: (params) => {
const action = params.value as string
const color = action === 'start' ? 'success' : action === 'stop' || action === 'exit' ? 'error' : 'info'
const Icon = action === 'start' ? StartIcon : action === 'stop' || action === 'exit' ? StopIcon : InfoIcon
return (
<Chip
size="small"
icon={<Icon sx={{ fontSize: '14px !important' }} />}
label={t(`common.${action}`)}
color={color as any}
variant="outlined"
sx={{ fontSize: 10, height: 20 }}
/>
)
}
},
{
field: 'status',
headerName: t('service_monitor.columns.status'),
width: 90,
renderCell: (params) => {
const status = params.value as string
return status === 'success' ? (
<SuccessIcon sx={{ color: 'success.main', fontSize: 18 }} />
) : status === 'failure' ? (
<ErrorIcon sx={{ color: 'error.main', fontSize: 18 }} />
) : (
<InfoIcon sx={{ color: 'info.main', fontSize: 18 }} />
)
}
},
{
field: 'details',
headerName: t('service_monitor.columns.details'),
flex: 1,
renderCell: (params) => (
<Tooltip title={params.value || ''}>
<Typography variant="caption" sx={{ color: 'rgba(255,255,255,0.5)', cursor: 'default' }}>
{params.value || '-'}
</Typography>
</Tooltip>
)
}
]
return (
<Box sx={{ height: '100%', display: 'flex', flexDirection: 'column', bgcolor: '#111418', color: '#fff' }}>
{/* Header */}
<Box sx={{ p: 2, display: 'flex', alignItems: 'center', justifyContent: 'space-between', borderBottom: '1px solid rgba(255,255,255,0.05)' }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1.5 }}>
<HistoryIcon color="primary" />
<Box>
<Typography variant="h6" sx={{ fontWeight: 800, lineHeight: 1 }}>
{t('service_monitor.title')}
</Typography>
<Typography variant="caption" sx={{ color: 'rgba(255,255,255,0.4)' }}>
{t('service_monitor.desc')}
</Typography>
</Box>
</Box>
<Stack direction="row" spacing={1}>
<IconButton onClick={fetchEvents} size="small" sx={{ color: 'rgba(255,255,255,0.5)' }}>
<RefreshIcon fontSize="small" />
</IconButton>
<IconButton onClick={handleClear} size="small" sx={{ color: '#f44336' }}>
<ClearIcon fontSize="small" />
</IconButton>
{onClose && (
<IconButton onClick={onClose} size="small" sx={{ color: 'rgba(255,255,255,0.5)' }}>
<CloseIcon fontSize="small" />
</IconButton>
)}
</Stack>
</Box>
{/* Content */}
<Box sx={{ flexGrow: 1, overflow: 'auto', p: 2 }}>
{loading && events.length === 0 ? (
<Box sx={{ display: 'flex', justifyContent: 'center', p: 5 }}>
<CircularProgress size={30} />
</Box>
) : events.length === 0 ? (
<Alert severity="info" sx={{ bgcolor: 'rgba(2, 136, 209, 0.05)', color: 'info.light' }}>
{t('service_monitor.no_events')}
</Alert>
) : (
<Stack spacing={3}>
{/* Chart Area */}
<Paper sx={{ p: 2, bgcolor: 'rgba(255,255,255,0.02)', borderRadius: 3, border: '1px solid rgba(255,255,255,0.05)' }}>
<Typography variant="subtitle2" sx={{ mb: 2, px: 1, fontWeight: 'bold', opacity: 0.6 }}>
{t('service_monitor.chart_title')}
</Typography>
<Box sx={{ height: 200, width: '100%' }}>
<BarChart
dataset={chartData}
xAxis={[{ scaleType: 'band', dataKey: 'service' }]}
series={[
{ dataKey: 'start', label: t('common.start'), color: '#4caf50' },
{ dataKey: 'stop', label: t('common.stop'), color: '#f44336' },
{ dataKey: 'error', label: t('common.error'), color: '#ff9800' }
]}
slotProps={{
legend: {
labelStyle: { fill: '#fff', fontSize: 10 },
direction: 'row',
position: { vertical: 'bottom', horizontal: 'middle' },
padding: -10,
}
}}
margin={{ top: 10, bottom: 50, left: 30, right: 10 }}
sx={{
'& .MuiChartsAxis-left .MuiChartsAxis-tickLabel': { fill: 'rgba(255,255,255,0.3)' },
'& .MuiChartsAxis-bottom .MuiChartsAxis-tickLabel': { fill: 'rgba(255,255,255,0.3)', fontSize: 10 },
'& .MuiChartsAxis-line': { stroke: 'rgba(255,255,255,0.1)' },
'& .MuiChartsAxis-tick': { stroke: 'rgba(255,255,255,0.1)' }
}}
/>
</Box>
</Paper>
{/* Logs Area */}
<Box sx={{ height: 400, width: '100%' }}>
<DataGrid
rows={events}
columns={columns}
density="compact"
disableRowSelectionOnClick
hideFooter
sx={{
border: 'none',
color: '#fff',
'& .MuiDataGrid-cell': { borderBottom: '1px solid rgba(255,255,255,0.03)', fontSize: 12 },
'& .MuiDataGrid-columnHeaders': { bgcolor: 'rgba(255,255,255,0.03)', borderBottom: '1px solid rgba(255,255,255,0.1)' },
'& .MuiDataGrid-columnHeaderTitle': { fontWeight: 'bold', fontSize: 11, opacity: 0.5 },
'& .MuiDataGrid-virtualScroller': { bgcolor: 'transparent' },
'& .MuiDataGrid-footerContainer': { borderTop: '1px solid rgba(255,255,255,0.1)' }
}}
/>
</Box>
</Stack>
)}
</Box>
</Box>
)
}
+18 -1
View File
@@ -47,7 +47,9 @@
"stopping": "Stopping", "stopping": "Stopping",
"reset_confirm_title": "Are you sure?", "reset_confirm_title": "Are you sure?",
"reset_confirm_text": "Do you want to reset {{service}} data? This operation deletes all data and performs a clean installation.", "reset_confirm_text": "Do you want to reset {{service}} data? This operation deletes all data and performs a clean installation.",
"reset_btn": "Yes, Reset" "reset_btn": "Yes, Reset",
"operating": "Operation in progress...",
"please_wait": "Please wait..."
}, },
"dashboard": { "dashboard": {
"title": "Dashboard", "title": "Dashboard",
@@ -305,5 +307,20 @@
"max_allowed_packet": "Max Allowed Packet", "max_allowed_packet": "Max Allowed Packet",
"max_allowed_packet_desc": "Maximum size of one packet or any generated/intermediate string." "max_allowed_packet_desc": "Maximum size of one packet or any generated/intermediate string."
} }
},
"service_monitor": {
"title": "Service Monitor",
"desc": "Track the start, stop, and error history of services.",
"event_log": "Event Log",
"clear_confirm": "Are you sure you want to clear the entire event history?",
"chart_title": "Service Activity Chart",
"no_events": "No events recorded yet.",
"columns": {
"service": "Service",
"action": "Action",
"status": "Status",
"timestamp": "Time",
"details": "Details"
}
} }
} }
+19 -2
View File
@@ -47,7 +47,9 @@
"stop_status": "Durduruluyor", "stop_status": "Durduruluyor",
"reset_confirm_title": "Emin misiniz?", "reset_confirm_title": "Emin misiniz?",
"reset_confirm_text": "{{service}} verilerini sıfırlamak istiyor musunuz? Bu işlem tüm verileri siler ve temiz kurulum yapar.", "reset_confirm_text": "{{service}} verilerini sıfırlamak istiyor musunuz? Bu işlem tüm verileri siler ve temiz kurulum yapar.",
"reset_btn": "Evet, Sıfırla" "reset_btn": "Evet, Sıfırla",
"operating": "İşlem devam ediyor...",
"please_wait": "Lütfen bekleyin..."
}, },
"dashboard": { "dashboard": {
"title": "Özet", "title": "Özet",
@@ -298,7 +300,22 @@
"key_buffer_size": "Anahtar Tampon Boyutu", "key_buffer_size": "Anahtar Tampon Boyutu",
"key_buffer_size_desc": "MyISAM tabloları için indeks blokları (tüm iş parçacıkları tarafından paylaşılır).", "key_buffer_size_desc": "MyISAM tabloları için indeks blokları (tüm iş parçacıkları tarafından paylaşılır).",
"max_allowed_packet": "Maksimum Paket Boyutu", "max_allowed_packet": "Maksimum Paket Boyutu",
"max_allowed_packet_desc": "Bir paketin veya oluşturulan herhangi bir dizenin maksimum boyutu." "max_allowed_packet_desc": "Tek bir SQL sorgusu veya sonucunun maksimum boyutu."
}
},
"service_monitor": {
"title": "Servis İzleyici",
"desc": "Servislerin başlama, durma ve hata geçmişini takip edin.",
"event_log": "Olay Günlüğü",
"clear_confirm": "Tüm olay geçmişini silmek istediğinize emin misiniz?",
"chart_title": "Servis Aktivite Grafiği",
"no_events": "Henüz bir olay kaydedilmemiş.",
"columns": {
"service": "Servis",
"action": "Eylem",
"status": "Durum",
"timestamp": "Zaman",
"details": "Detay"
} }
} }
} }