zacetni test
This commit is contained in:
22
Dockerfile
Normal file
22
Dockerfile
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# Use official Python base image
|
||||||
|
FROM python:3.11-slim
|
||||||
|
|
||||||
|
# Set environment variables
|
||||||
|
|
||||||
|
# Set working directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy requirements file
|
||||||
|
COPY requirements.txt .
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
RUN pip install --upgrade pip && pip install -r requirements.txt
|
||||||
|
|
||||||
|
# Copy the rest of the app
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Expose port (change if needed)
|
||||||
|
EXPOSE 8000
|
||||||
|
|
||||||
|
# Run the application
|
||||||
|
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||||
BIN
__pycache__/main.cpython-313.pyc
Normal file
BIN
__pycache__/main.cpython-313.pyc
Normal file
Binary file not shown.
143
index.html
Normal file
143
index.html
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>System Monitor</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<style>
|
||||||
|
body { font-family: Arial, sans-serif; background: #f4f4f4; margin: 0; padding: 0; }
|
||||||
|
.container { max-width: 600px; margin: 40px auto; background: #fff; padding: 24px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);}
|
||||||
|
h1 { text-align: center; }
|
||||||
|
.stat { display: flex; justify-content: space-between; margin: 16px 0; font-size: 1.2em; }
|
||||||
|
.label { color: #555; }
|
||||||
|
.value { font-weight: bold; }
|
||||||
|
.refresh-btn { display: block; margin: 24px auto 0; padding: 8px 24px; font-size: 1em; border: none; border-radius: 4px; background: #0078d4; color: #fff; cursor: pointer; }
|
||||||
|
.refresh-btn:hover { background: #005fa3; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h1>System Monitor</h1>
|
||||||
|
<div class="stat"><span class="label">CPU Usage:</span> <span class="value" id="cpu">--</span></div>
|
||||||
|
<div class="stat"><span class="label">RAM Usage:</span> <span class="value" id="ram">--</span></div>
|
||||||
|
<div class="stat"><span class="label">SSD Usage:</span> <span class="value" id="disk">--</span></div>
|
||||||
|
<div class="stat"><span class="label">Uptime:</span> <span class="value" id="uptime">--</span></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="container" style="margin-top:24px;">
|
||||||
|
<h1>Wall Messages</h1>
|
||||||
|
<form id="addMessageForm" style="margin-bottom:16px;">
|
||||||
|
<input type="text" id="newMessage" placeholder="Type a message..." style="width:70%;padding:8px;" required>
|
||||||
|
<button type="submit" class="refresh-btn" style="display:inline-block;margin:0;">Add</button>
|
||||||
|
</form>
|
||||||
|
<ul id="messages" style="list-style:none;padding:0;"></ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
async function fetchStats() {
|
||||||
|
try {
|
||||||
|
const [cpu, ram, disk, uptime] = await Promise.all([
|
||||||
|
fetch('/cpu').then(res => res.json()),
|
||||||
|
fetch('/ram').then(res => res.json()),
|
||||||
|
fetch('/disk').then(res => res.json()),
|
||||||
|
fetch('/uptime').then(res => res.json())
|
||||||
|
]);
|
||||||
|
document.getElementById('cpu').textContent = cpu.cpu_percent + '%';
|
||||||
|
document.getElementById('ram').textContent = ram.percent + '%';
|
||||||
|
document.getElementById('disk').textContent = disk.percent + '%';
|
||||||
|
document.getElementById('uptime').textContent = uptime.uptime_seconds + ' s';
|
||||||
|
} catch (e) {
|
||||||
|
document.getElementById('cpu').textContent = 'Error';
|
||||||
|
document.getElementById('ram').textContent = 'Error';
|
||||||
|
document.getElementById('disk').textContent = 'Error';
|
||||||
|
document.getElementById('uptime').textContent = 'Error';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wall Messages CRUD
|
||||||
|
async function fetchMessages() {
|
||||||
|
const res = await fetch('/wall');
|
||||||
|
const messages = await res.json();
|
||||||
|
const ul = document.getElementById('messages');
|
||||||
|
ul.innerHTML = '';
|
||||||
|
messages.forEach(msg => {
|
||||||
|
const li = document.createElement('li');
|
||||||
|
li.style.display = 'flex';
|
||||||
|
li.style.justifyContent = 'space-between';
|
||||||
|
li.style.alignItems = 'center';
|
||||||
|
li.style.padding = '8px 0';
|
||||||
|
|
||||||
|
const span = document.createElement('span');
|
||||||
|
span.textContent = msg.text;
|
||||||
|
|
||||||
|
const editBtn = document.createElement('button');
|
||||||
|
editBtn.textContent = 'Edit';
|
||||||
|
editBtn.className = 'refresh-btn';
|
||||||
|
editBtn.style.background = '#ffa500';
|
||||||
|
editBtn.style.marginRight = '8px';
|
||||||
|
editBtn.onclick = () => editMessage(msg);
|
||||||
|
|
||||||
|
const delBtn = document.createElement('button');
|
||||||
|
delBtn.textContent = 'Delete';
|
||||||
|
delBtn.className = 'refresh-btn';
|
||||||
|
delBtn.style.background = '#d40000';
|
||||||
|
delBtn.onclick = () => deleteMessage(msg.id);
|
||||||
|
|
||||||
|
const btns = document.createElement('span');
|
||||||
|
btns.appendChild(editBtn);
|
||||||
|
btns.appendChild(delBtn);
|
||||||
|
|
||||||
|
li.appendChild(span);
|
||||||
|
li.appendChild(btns);
|
||||||
|
ul.appendChild(li);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function addMessage(text) {
|
||||||
|
await fetch('/wall', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
body: JSON.stringify({text})
|
||||||
|
});
|
||||||
|
fetchMessages();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteMessage(id) {
|
||||||
|
await fetch(`/wall/${id}`, { method: 'DELETE' });
|
||||||
|
fetchMessages();
|
||||||
|
}
|
||||||
|
|
||||||
|
function editMessage(msg) {
|
||||||
|
const newText = prompt('Edit message:', msg.text);
|
||||||
|
if (newText !== null && newText.trim() !== '') {
|
||||||
|
updateMessage(msg.id, newText.trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateMessage(id, text) {
|
||||||
|
await fetch(`/wall/${id}`, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
body: JSON.stringify({text})
|
||||||
|
});
|
||||||
|
fetchMessages();
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('addMessageForm').onsubmit = function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const input = document.getElementById('newMessage');
|
||||||
|
const text = input.value.trim();
|
||||||
|
if (text) {
|
||||||
|
addMessage(text);
|
||||||
|
input.value = '';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.onload = function() {
|
||||||
|
fetchStats();
|
||||||
|
setInterval(fetchStats, 5000); // Fetch every 5 seconds
|
||||||
|
fetchMessages();
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
149
main.py
Normal file
149
main.py
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
from fastapi import FastAPI
|
||||||
|
import psutil
|
||||||
|
import time
|
||||||
|
import datetime
|
||||||
|
from pydantic import BaseModel
|
||||||
|
from sqlalchemy import create_engine, Column, Integer, String, DateTime
|
||||||
|
from sqlalchemy.orm import sessionmaker, declarative_base
|
||||||
|
from fastapi import HTTPException
|
||||||
|
import os
|
||||||
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
from fastapi.staticfiles import StaticFiles
|
||||||
|
|
||||||
|
if os.environ.get("DATABASE_URL") == None:
|
||||||
|
os.environ["DATABASE_URL"] = "sqlite:///./wall_messages.db"
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
start_time = time.time()
|
||||||
|
@app.get("/uptime")
|
||||||
|
def get_uptime():
|
||||||
|
uptime_seconds = int(time.time() - start_time)
|
||||||
|
uptime_str = str(datetime.timedelta(seconds=uptime_seconds))
|
||||||
|
return {"uptime_seconds": uptime_seconds, "uptime": uptime_str}
|
||||||
|
|
||||||
|
@app.get("/disk")
|
||||||
|
def get_disk_usage():
|
||||||
|
usage = psutil.disk_usage('/')
|
||||||
|
return {
|
||||||
|
"total": usage.total,
|
||||||
|
"used": usage.used,
|
||||||
|
"free": usage.free,
|
||||||
|
"percent": usage.percent
|
||||||
|
}
|
||||||
|
|
||||||
|
@app.get("/ram")
|
||||||
|
def get_ram_usage():
|
||||||
|
mem = psutil.virtual_memory()
|
||||||
|
return {
|
||||||
|
"total": mem.total,
|
||||||
|
"available": mem.available,
|
||||||
|
"used": mem.used,
|
||||||
|
"percent": mem.percent
|
||||||
|
}
|
||||||
|
@app.get("/cpu")
|
||||||
|
def get_cpu_usage():
|
||||||
|
cpu_percent = psutil.cpu_percent(interval=1)
|
||||||
|
return {"cpu_percent": cpu_percent}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
DATABASE_URL = "sqlite:///./wall_messages.db"
|
||||||
|
|
||||||
|
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
|
||||||
|
SessionLocal = sessionmaker(bind=engine)
|
||||||
|
Base = declarative_base()
|
||||||
|
|
||||||
|
class WallMessageDB(Base):
|
||||||
|
__tablename__ = "wall_messages"
|
||||||
|
id = Column(Integer, primary_key=True, index=True)
|
||||||
|
text = Column(String, nullable=False)
|
||||||
|
timestamp = Column(DateTime, default=datetime.datetime.utcnow(), nullable=False)
|
||||||
|
|
||||||
|
Base.metadata.create_all(bind=engine)
|
||||||
|
|
||||||
|
class WallMessageCreate(BaseModel):
|
||||||
|
text: str
|
||||||
|
|
||||||
|
class WallMessageRead(BaseModel):
|
||||||
|
id: int
|
||||||
|
text: str
|
||||||
|
timestamp: datetime.datetime
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
orm_mode = True
|
||||||
|
|
||||||
|
@app.post("/wall", response_model=WallMessageRead)
|
||||||
|
def post_wall_message(msg: WallMessageCreate):
|
||||||
|
db = SessionLocal()
|
||||||
|
db_msg = WallMessageDB(text=msg.text)
|
||||||
|
db.add(db_msg)
|
||||||
|
db.commit()
|
||||||
|
db.refresh(db_msg)
|
||||||
|
db.close()
|
||||||
|
return db_msg
|
||||||
|
|
||||||
|
@app.get("/wall", response_model=list[WallMessageRead])
|
||||||
|
def get_wall_messages():
|
||||||
|
db = SessionLocal()
|
||||||
|
messages = db.query(WallMessageDB).order_by(WallMessageDB.timestamp.desc()).all()
|
||||||
|
db.close()
|
||||||
|
return messages
|
||||||
|
|
||||||
|
@app.get("/wall/{msg_id}", response_model=WallMessageRead)
|
||||||
|
def get_wall_message(msg_id: int):
|
||||||
|
db = SessionLocal()
|
||||||
|
msg = db.query(WallMessageDB).filter(WallMessageDB.id == msg_id).first()
|
||||||
|
db.close()
|
||||||
|
if not msg:
|
||||||
|
raise HTTPException(status_code=404, detail="Message not found")
|
||||||
|
return msg
|
||||||
|
|
||||||
|
@app.put("/wall/{msg_id}", response_model=WallMessageRead)
|
||||||
|
def update_wall_message(msg_id: int, msg: WallMessageCreate):
|
||||||
|
db = SessionLocal()
|
||||||
|
db_msg = db.query(WallMessageDB).filter(WallMessageDB.id == msg_id).first()
|
||||||
|
if not db_msg:
|
||||||
|
db.close()
|
||||||
|
raise HTTPException(status_code=404, detail="Message not found")
|
||||||
|
db_msg.text = msg.text
|
||||||
|
db.commit()
|
||||||
|
db.refresh(db_msg)
|
||||||
|
db.close()
|
||||||
|
return db_msg
|
||||||
|
|
||||||
|
@app.delete("/wall/{msg_id}")
|
||||||
|
def delete_wall_message(msg_id: int):
|
||||||
|
db = SessionLocal()
|
||||||
|
db_msg = db.query(WallMessageDB).filter(WallMessageDB.id == msg_id).first()
|
||||||
|
if not db_msg:
|
||||||
|
db.close()
|
||||||
|
raise HTTPException(status_code=404, detail="Message not found")
|
||||||
|
db.delete(db_msg)
|
||||||
|
db.commit()
|
||||||
|
db.close()
|
||||||
|
return {"status": "success", "message": f"Deleted message {msg_id}"}
|
||||||
|
|
||||||
|
class WallMessage(BaseModel):
|
||||||
|
message: str
|
||||||
|
|
||||||
|
wall_messages = []
|
||||||
|
|
||||||
|
@app.post("/wall")
|
||||||
|
def post_wall_message(msg: WallMessage):
|
||||||
|
wall_messages.append(msg.message)
|
||||||
|
return {"status": "success", "message": msg.message}
|
||||||
|
|
||||||
|
@app.get("/wall")
|
||||||
|
def get_wall_messages():
|
||||||
|
return wall_messages
|
||||||
|
|
||||||
|
app.add_middleware(
|
||||||
|
CORSMiddleware,
|
||||||
|
allow_origins=["*"],
|
||||||
|
allow_credentials=True,
|
||||||
|
allow_methods=["*"],
|
||||||
|
allow_headers=["*"],
|
||||||
|
)
|
||||||
|
app.mount("/", StaticFiles(directory=".", html=True), name="static")
|
||||||
16
requirements.txt
Normal file
16
requirements.txt
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
annotated-types==0.7.0
|
||||||
|
anyio==4.9.0
|
||||||
|
click==8.2.0
|
||||||
|
fastapi==0.115.12
|
||||||
|
greenlet==3.2.2
|
||||||
|
h11==0.16.0
|
||||||
|
idna==3.10
|
||||||
|
psutil==7.0.0
|
||||||
|
pydantic==2.11.4
|
||||||
|
pydantic_core==2.33.2
|
||||||
|
sniffio==1.3.1
|
||||||
|
SQLAlchemy==2.0.40
|
||||||
|
starlette==0.46.2
|
||||||
|
typing-inspection==0.4.0
|
||||||
|
typing_extensions==4.13.2
|
||||||
|
uvicorn==0.34.2
|
||||||
BIN
wall_messages.db
Normal file
BIN
wall_messages.db
Normal file
Binary file not shown.
Reference in New Issue
Block a user