Add API req to create new app/section
This commit is contained in:
parent
59027c9ba6
commit
57d1156425
Binary file not shown.
@ -1,24 +1,26 @@
|
|||||||
- name: Gitea
|
sections:
|
||||||
url: https://git.dvirlabs.com
|
- apps:
|
||||||
icon: http://192.168.10.118:1111/icons/gitea.svg
|
- description: Dashboards
|
||||||
description: Git server
|
|
||||||
|
|
||||||
- name: Grafana
|
|
||||||
url: https://grafana.dvirlabs.com
|
|
||||||
icon: http://192.168.10.118:1111/icons/grafana.svg
|
icon: http://192.168.10.118:1111/icons/grafana.svg
|
||||||
description: Dashboards
|
name: Grafana
|
||||||
|
url: https://grafana.dvirlabs.com
|
||||||
- name: Prometheus
|
- description: Monitoring
|
||||||
url: https://prometheus.dvirlabs.com
|
|
||||||
icon: http://192.168.10.118:1111/icons/prometheus.svg
|
icon: http://192.168.10.118:1111/icons/prometheus.svg
|
||||||
description: Monitoring
|
name: Prometheus
|
||||||
|
url: https://prometheus.dvirlabs.com
|
||||||
- name: Harbor
|
name: Monitoring
|
||||||
url: https://harbor.dvirlabs.com
|
- apps:
|
||||||
|
- description: Git server
|
||||||
|
icon: http://192.168.10.118:1111/icons/gitea.svg
|
||||||
|
name: Gitea
|
||||||
|
url: https://git.dvirlabs.com
|
||||||
|
- description: Container registry
|
||||||
icon: http://192.168.10.118:1111/icons/harbor.svg
|
icon: http://192.168.10.118:1111/icons/harbor.svg
|
||||||
description: Container registry
|
name: Harbor
|
||||||
|
url: https://harbor.dvirlabs.com
|
||||||
- name: Woodpecker
|
- description: CI/CD
|
||||||
url: https://woodpecker.dvirlabs.com
|
|
||||||
icon: http://192.168.10.118:1111/icons/woodpecker-ci.svg
|
icon: http://192.168.10.118:1111/icons/woodpecker-ci.svg
|
||||||
description: CI/CD
|
name: Woodpecker
|
||||||
|
url: https://woodpecker.dvirlabs.com
|
||||||
|
name: Dev-tools
|
||||||
|
|
||||||
|
|||||||
@ -2,6 +2,8 @@ from fastapi import FastAPI
|
|||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
import yaml
|
import yaml
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
# Allow CORS for all origins
|
# Allow CORS for all origins
|
||||||
@ -27,6 +29,47 @@ def get_apps():
|
|||||||
with open(APPS_FILE, "r") as f:
|
with open(APPS_FILE, "r") as f:
|
||||||
return yaml.safe_load(f)
|
return yaml.safe_load(f)
|
||||||
|
|
||||||
|
class AppEntry(BaseModel):
|
||||||
|
section: str
|
||||||
|
name: str
|
||||||
|
icon: str
|
||||||
|
description: str
|
||||||
|
url: str
|
||||||
|
|
||||||
|
@app.post("/add_app")
|
||||||
|
def add_app(entry: AppEntry):
|
||||||
|
if not APPS_FILE.exists():
|
||||||
|
current = {"sections": []}
|
||||||
|
else:
|
||||||
|
with open(APPS_FILE, "r") as f:
|
||||||
|
current = yaml.safe_load(f) or {"sections": []}
|
||||||
|
|
||||||
|
# Find or create section
|
||||||
|
for section in current["sections"]:
|
||||||
|
if section["name"] == entry.section:
|
||||||
|
section["apps"].append({
|
||||||
|
"name": entry.name,
|
||||||
|
"icon": entry.icon,
|
||||||
|
"description": entry.description,
|
||||||
|
"url": entry.url,
|
||||||
|
})
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
current["sections"].append({
|
||||||
|
"name": entry.section,
|
||||||
|
"apps": [{
|
||||||
|
"name": entry.name,
|
||||||
|
"icon": entry.icon,
|
||||||
|
"description": entry.description,
|
||||||
|
"url": entry.url,
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
|
||||||
|
with open(APPS_FILE, "w") as f:
|
||||||
|
yaml.safe_dump(current, f)
|
||||||
|
|
||||||
|
return {"status": "added"}
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import uvicorn
|
import uvicorn
|
||||||
uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)
|
uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)
|
||||||
@ -1,20 +1,22 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import './App.css';
|
import './App.css';
|
||||||
import AppGrid from './components/AppGrid';
|
import SectionGrid from './components/SectionGrid';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [apps, setApps] = useState([]);
|
const [sections, setSections] = useState([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetch('/apps')
|
fetch('/apps')
|
||||||
.then(res => res.json())
|
.then(res => res.json())
|
||||||
.then(data => setApps(data));
|
.then(data => setSections(data.sections || []));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="App">
|
<div className="App">
|
||||||
<h1 className="main-title">🔷 Navix</h1>
|
<h1 className="main-title">🔷 Navix</h1>
|
||||||
<AppGrid apps={apps} />
|
{sections.map((section) => (
|
||||||
|
<SectionGrid key={section.name} section={section} />
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
13
frontend/src/components/SectionGrid.jsx
Normal file
13
frontend/src/components/SectionGrid.jsx
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import AppGrid from './AppGrid';
|
||||||
|
import '../style/SectionGrid.css';
|
||||||
|
|
||||||
|
function SectionGrid({ section }) {
|
||||||
|
return (
|
||||||
|
<div className="section">
|
||||||
|
<h2 className="section-title">{section.name}</h2>
|
||||||
|
<AppGrid apps={section.apps} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SectionGrid;
|
||||||
14
frontend/src/style/SectionGrid.css
Normal file
14
frontend/src/style/SectionGrid.css
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
.section {
|
||||||
|
margin-bottom: 3rem;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 1000px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
color: #ffffff;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
border-bottom: 1px solid #444;
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user