Skip to content

Commit

Permalink
CPU, Memory stat
Browse files Browse the repository at this point in the history
  • Loading branch information
aceberg committed Feb 26, 2025
1 parent 71a59d5 commit 0a28202
Show file tree
Hide file tree
Showing 14 changed files with 169 additions and 90 deletions.
5 changes: 5 additions & 0 deletions backend/internal/models/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,16 @@ type Conf struct {

// Item - one service or container
type Item struct {
ID int `yaml:"ID"`
Group string `yaml:"group"`
Name string `yaml:"name"`
Type string `yaml:"type"`
Link string `yaml:"link,omitempty"`
Icon string `yaml:"icon,omitempty"`
Exec string `yaml:"-"`
State string `yaml:"-"`
Mem string `yaml:"-"`
CPU string `yaml:"-"`
}

// TypeStruct - one type struct
Expand All @@ -32,4 +35,6 @@ type TypeStruct struct {
Stop string
Logs string
State string
Mem string
CPU string
}
3 changes: 2 additions & 1 deletion backend/internal/web/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,13 @@ func apiExec(c *gin.Context) {
func apiGetItems(c *gin.Context) {

items := yaml.Read(appConfig.ItemPath)
items = getAllStates(items)

sort.Slice(items, func(i, j int) bool {
return items[i].Name < items[j].Name
})

items = setItemIDs(items)

c.IndentedJSON(http.StatusOK, items)
}

Expand Down
47 changes: 9 additions & 38 deletions backend/internal/web/functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,8 @@ package web

import (
// "log"
"sync"

"github.com/aceberg/AnyAppStart/internal/models"
"github.com/aceberg/AnyAppStart/internal/service"
"github.com/aceberg/AnyAppStart/internal/yaml"
)

func typesToStruct(types map[string]map[string]string) (typeStructArray []models.TypeStruct) {
Expand All @@ -21,6 +18,8 @@ func typesToStruct(types map[string]map[string]string) (typeStructArray []models
oneStruct.Stop = value["Stop"]
oneStruct.Logs = value["Logs"]
oneStruct.State = value["State"]
oneStruct.Mem = value["Mem"]
oneStruct.CPU = value["CPU"]

typeStructArray = append(typeStructArray, oneStruct)
}
Expand All @@ -37,46 +36,18 @@ func toOneType(tStruct models.TypeStruct) (tmpMap map[string]string) {
tmpMap["Restart"] = tStruct.Restart
tmpMap["Logs"] = tStruct.Logs
tmpMap["State"] = tStruct.State
tmpMap["Mem"] = tStruct.Mem
tmpMap["CPU"] = tStruct.CPU

return tmpMap
}

func getAllStates(items []models.Item) (newItems []models.Item) {
var wg sync.WaitGroup
var counter int
func setItemIDs(items []models.Item) []models.Item {
var newItems []models.Item

types := yaml.ReadTypes(appConfig.TypePath)
newItems = []models.Item{}

for _, item := range items {

counter++
wg.Add(1)
go func() {
defer wg.Done()
item = getOneState(item, types)
// log.Println("Getting state for Item", item.Name)
newItems = append(newItems, item)
counter--
}()
if counter > 3 {
wg.Wait()
}
for i, item := range items {
item.ID = i
newItems = append(newItems, item)
}
wg.Wait()

return newItems
}

func getOneState(item models.Item, types map[string]map[string]string) models.Item {

item.Exec = "State"
ok, _ := service.Exec(item, types)
if ok {
item.State = "on"
} else {
item.State = "off"
}

return item
}
40 changes: 20 additions & 20 deletions backend/internal/web/public/assets/index.js

Large diffs are not rendered by default.

23 changes: 23 additions & 0 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,32 @@ import "bootstrap/js/dist/dropdown";
import './App.css'
import Body from './components/Body'
import Header from './components/Header'
import { useEffect } from "react";
import { updAllItems } from "./functions/updstate";
import { getItems } from "./functions/api";
import mobxStore from "./functions/store";

function App() {

const fetchData = async () => {

const items = await getItems();
mobxStore.setItemList(items);
mobxStore.setUpdBody(true);
}

useEffect(() => {
fetchData();

setTimeout(() => {
updAllItems();
}, 1000);

setInterval(() => {
updAllItems();
}, 60000); // 60000 ms = 1 minute
}, []);

return (
<div className="container-lg mb-2">
<Header></Header>
Expand Down
26 changes: 13 additions & 13 deletions frontend/src/components/Body.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { useEffect, useState } from "react"
import { getItems } from "../functions/api"
import ItemShow from "./ItemShow";
import { filterItems, getGroupsList, sortItems } from "../functions/sortitems";
import BodyTabs from "./BodyTabs";
Expand All @@ -10,7 +9,9 @@ import BodyGroupFilter from "./BodyGroupFilter";

const Body: React.FC = observer(() => {

const [items, setItems] = useState<Item[]>([]);
const stateOn = "bi bi-circle-fill text-success";
const stateOff = "bi bi-circle-fill text-danger";

const [grList, setGrList] = useState<string[]>([]);
const [sortTrigger, setSortTrigger] = useState<boolean>(false);

Expand All @@ -19,18 +20,18 @@ const Body: React.FC = observer(() => {
if (sortBy === mobxStore.sortField) {
mobxStore.setSortWay(!mobxStore.sortWay);
}
setItems(sortItems(items, sortBy, mobxStore.sortWay, sortTrigger));
mobxStore.setItemFiltered(sortItems(mobxStore.itemFiltered, sortBy, mobxStore.sortWay, sortTrigger));
mobxStore.setSortField(sortBy);
}

const fetchData = async () => {
const fetchData = () => {

let tmpItems:Item[] = await getItems();
let tmpItems:Item[] = mobxStore.itemList;
setGrList(getGroupsList(tmpItems));
tmpItems = filterItems(tmpItems, "Type", mobxStore.getFilterType());
tmpItems = filterItems(tmpItems, "Group", mobxStore.getFilterGroup());

setItems(sortItems(tmpItems, mobxStore.getSortField(), mobxStore.getSortWay(), sortTrigger));
mobxStore.setItemFiltered(sortItems(tmpItems, mobxStore.getSortField(), mobxStore.getSortWay(), sortTrigger));
};

useEffect(() => {
Expand All @@ -39,12 +40,6 @@ const Body: React.FC = observer(() => {
// console.log("BODY UPD");
}, [mobxStore.updBody]);

useEffect(() => { // Regular update
setInterval(() => {
fetchData();
}, 60000); // 60000 ms = 1 minute
}, []);

return (
<div className="row mt-2">
<div className="col-md">
Expand All @@ -58,6 +53,8 @@ const Body: React.FC = observer(() => {
<tr>
<th style={{ width: "1%" }}></th>
<th><i className="bi bi-circle"></i><i onClick={() => handleSort("State")} className="bi bi-sort-down-alt text-primary shade-hover"></i></th>
<th>CPU</th>
<th>Mem</th>
<th>Type<i onClick={() => handleSort("Type")} className="bi bi-sort-down-alt text-primary shade-hover"></i></th>
<th>Icon</th>
<th>Name<i onClick={() => handleSort("Name")} className="bi bi-sort-down-alt text-primary shade-hover"></i></th>
Expand All @@ -68,9 +65,12 @@ const Body: React.FC = observer(() => {
</tr>
</thead>
<tbody>
{items?.map((item, i) => (
{mobxStore.itemFiltered?.map((item, i) => (
<tr key={i}>
<td className="text-primary text-opacity-75">{i+1}.</td>
<td><i className={item.State == "on" ? stateOn : stateOff }></i></td>
<td>{item.CPU}</td>
<td>{item.Mem}</td>
<ItemShow item={item}></ItemShow>
</tr>
))}
Expand Down
5 changes: 4 additions & 1 deletion frontend/src/components/ConfigAddItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@ import EditItem from "./EditItem"
function ConfigAddItem(_props: any) {

const item:Item = {
ID: 0,
Group: "",
Name: "",
Type: "",
Link: "",
Icon: "",
State: "",
Exec: ""
Exec: "",
CPU: "",
Mem: "",
};

return (
Expand Down
5 changes: 4 additions & 1 deletion frontend/src/components/EditItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@ import { Item } from "../functions/exports";
function EditItem(_props: any) {

const item:Item = {
ID: _props.item.ID,
Group: _props.item.Group,
Name: _props.item.Name,
Type: _props.item.Type,
Link: _props.item.Link,
Icon: _props.item.Icon,
Exec: "",
State: ""
State: "",
CPU: "",
Mem: "",
};

const [isModalOpen, setModalOpen] = useState<boolean>(false);
Expand Down
13 changes: 5 additions & 8 deletions frontend/src/components/ItemShow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,21 @@ import Logs from "./Logs";
import EditItem from "./EditItem";
import mobxStore from "../functions/store";

function ItemShow(_props: any) {

const stateOn = "bi bi-circle-fill text-success";
const stateOff = "bi bi-circle-fill text-danger";
function ItemShow(_props: any) {

const handleExec = async (exec: string) => {
let item = _props.item;
item.Exec = exec;
_props.item.Exec = exec;

console.log("EXEC:", item);
await apiExec(item);
console.log("EXEC:", _props.item);
await apiExec(_props.item);
setTimeout(() => {
mobxStore.setUpdBody(true);
}, 1000);
}

return (
<>
<td><i className={_props.item.State == "on" ? stateOn : stateOff }></i></td>
<td>{_props.item.Type}</td>
<td>
{_props.item.Icon
Expand Down Expand Up @@ -56,3 +52,4 @@ function ItemShow(_props: any) {
}

export default ItemShow

2 changes: 2 additions & 0 deletions frontend/src/components/TypeAdd.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ function TypeAdd(_props:any) {
Restart: "",
Logs: "",
State: "",
CPU: "",
Mem: "",
}

return (
Expand Down
18 changes: 12 additions & 6 deletions frontend/src/components/TypeEdit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ function TypeEdit(_props: any) {
Stop: _props.typeItem.Stop,
Restart: _props.typeItem.Restart,
Logs: _props.typeItem.Logs,
State: _props.typeItem.State
State: _props.typeItem.State,
CPU: _props.typeItem.CPU,
Mem: _props.typeItem.Mem
};

const [isModalOpen, setModalOpen] = useState<boolean>(false);
Expand Down Expand Up @@ -70,15 +72,19 @@ function TypeEdit(_props: any) {
<input className="form-control mb-3" defaultValue={oldType.Name} id="nid" name="Name" onChange={handleChange} placeholder="Not empty string"></input>
<p>Use variable <code>$ITEMNAME</code> in the commands below</p>
<label htmlFor="gid" className="form-label text-primary">Start</label>
<input className="form-control mb-3" defaultValue={oldType.Start} id="gid" name="Start" onChange={handleChange} placeholder="Example: docker start $ITEMNAME"></input>
<input className="form-control mb-3" defaultValue={oldType.Start} id="gid" name="Start" onChange={handleChange} placeholder="docker start $ITEMNAME"></input>
<label htmlFor="oid" className="form-label text-primary">Stop</label>
<input className="form-control mb-3" defaultValue={oldType.Stop} id="oid" name="Stop" onChange={handleChange} placeholder="Example: docker stop $ITEMNAME"></input>
<input className="form-control mb-3" defaultValue={oldType.Stop} id="oid" name="Stop" onChange={handleChange} placeholder="docker stop $ITEMNAME"></input>
<label htmlFor="rid" className="form-label text-primary">Restart</label>
<input className="form-control mb-3" defaultValue={oldType.Restart} id="rid" name="Restart" onChange={handleChange} placeholder="Example: docker restart $ITEMNAME"></input>
<input className="form-control mb-3" defaultValue={oldType.Restart} id="rid" name="Restart" onChange={handleChange} placeholder="docker restart $ITEMNAME"></input>
<label htmlFor="lid" className="form-label text-primary">Logs</label>
<input className="form-control mb-3" defaultValue={oldType.Logs} id="lid" name="Logs" onChange={handleChange} placeholder="Example: docker logs $ITEMNAME"></input>
<input className="form-control mb-3" defaultValue={oldType.Logs} id="lid" name="Logs" onChange={handleChange} placeholder="docker logs $ITEMNAME"></input>
<label htmlFor="tid" className="form-label text-primary">State</label>
<input className="form-control mb-3" defaultValue={oldType.State} id="tid" name="State" onChange={handleChange} placeholder="Example: docker ps --filter status=running | grep $ITEMNAME"></input>
<input className="form-control mb-3" defaultValue={oldType.State} id="tid" name="State" onChange={handleChange} placeholder="docker ps --filter status=running | grep $ITEMNAME"></input>
<label htmlFor="cpuid" className="form-label text-primary">CPU</label>
<input className="form-control mb-3" defaultValue={oldType.CPU} id="cpuid" name="CPU" onChange={handleChange} placeholder="docker stats --no-stream --format '{{ .CPUPerc }}' $ITEMNAME"></input>
<label htmlFor="memid" className="form-label text-primary">Mem</label>
<input className="form-control mb-3" defaultValue={oldType.Mem} id="memid" name="Mem" onChange={handleChange} placeholder="docker stats --no-stream --format '{{ .MemUsage }}' $ITEMNAME | awk '{print $1}'"></input>
<hr></hr>
<div className='d-flex justify-content-between'>
<button className="btn btn-danger" type="button" onClick={handleDel}>Delete</button>
Expand Down
5 changes: 5 additions & 0 deletions frontend/src/functions/exports.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
export interface Item {
ID: number;
Group: string;
Name: string;
Type: string;
Link: string;
Icon: string;
Exec: string;
State: string;
Mem: string;
CPU: string;
};

export interface Conf {
Expand All @@ -23,4 +26,6 @@ export interface TypeStruct {
Restart: string;
Logs: string;
State: string;
CPU: string;
Mem: string;
};
25 changes: 23 additions & 2 deletions frontend/src/functions/store.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,31 @@
import { makeAutoObservable } from 'mobx';
import { action, makeAutoObservable } from 'mobx';
import { Conf, Item, TypeStruct } from './exports';


class MobxStore {
constructor() {
makeAutoObservable(this);
makeAutoObservable(this, {
updItemList: action
});
}

itemList:Item[] = [];
setItemList(list:Item[]) {
this.itemList = list;
}
updItemList(newItem:Item) {
this.itemList.forEach((item, i) => {
if (item.ID == newItem.ID) {
action(() => {
this.itemList[i] = newItem
});
}
});
}

itemFiltered:Item[] = [];
setItemFiltered(list:Item[]) {
this.itemFiltered = list;
}

typeList:TypeStruct[] = [];
Expand Down
Loading

0 comments on commit 0a28202

Please sign in to comment.