inital commit
This commit is contained in:
commit
206faa3b89
5 changed files with 345 additions and 0 deletions
BIN
server/dbs/2025-05-04/dredstation.sqlite3
Normal file
BIN
server/dbs/2025-05-04/dredstation.sqlite3
Normal file
Binary file not shown.
108
server/server.py
Normal file
108
server/server.py
Normal file
|
@ -0,0 +1,108 @@
|
|||
# server.py
|
||||
import os, json, time, sqlite3
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
from flask import Flask, request, jsonify, send_file
|
||||
from flask_cors import CORS
|
||||
import re
|
||||
|
||||
AUTH_KEY = os.getenv("AUTH_KEY", "dred123")
|
||||
ROLLING_WINDOW_SECONDS = 3600 * 6 # 6 hours
|
||||
|
||||
app = Flask(__name__)
|
||||
CORS(app)
|
||||
|
||||
|
||||
def normalize_sqlite_key(key: str) -> str:
|
||||
# Replace anything not alphanum or underscore with underscore
|
||||
return re.sub(r'[^a-zA-Z0-9_]', '_', key)
|
||||
|
||||
|
||||
def get_db_path(hostname: str) -> str:
|
||||
today = datetime.now().strftime("%Y-%m-%d")
|
||||
host_clean = hostname.replace(" ", "_").replace("-", "_")
|
||||
path = f"./dbs/{today}/{host_clean}.sqlite3"
|
||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||
return path
|
||||
|
||||
@app.route('/api/metrics', methods=['POST'])
|
||||
def receive_metrics():
|
||||
token = request.headers.get("Authorization", "").replace("Bearer ", "")
|
||||
if token != AUTH_KEY:
|
||||
return jsonify({"error": "unauthorized"}), 403
|
||||
|
||||
data = request.get_json()
|
||||
if not data:
|
||||
return jsonify({"error": "invalid JSON"}), 400
|
||||
|
||||
host = data.get("hostname") # crude host ID
|
||||
timestamp = int(data.get("time", time.time()))
|
||||
db_path = get_db_path(host)
|
||||
table_name = f"{host.replace('-', '_')}" # sanitize
|
||||
|
||||
with sqlite3.connect(db_path) as db:
|
||||
cursor = db.cursor()
|
||||
|
||||
# Create or alter table based on incoming keys
|
||||
raw_keys = list(data.keys())
|
||||
safe_keys = [normalize_sqlite_key(k) for k in raw_keys]
|
||||
key_map = dict(zip(raw_keys, safe_keys))
|
||||
|
||||
columns = ", ".join([f"\"{k}\" TEXT" for k in safe_keys])
|
||||
cursor.execute(f"""
|
||||
CREATE TABLE IF NOT EXISTS "{table_name}" (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
timestamp INTEGER,
|
||||
{columns}
|
||||
)
|
||||
""")
|
||||
|
||||
existing_cols = set(row[1] for row in cursor.execute(f"PRAGMA table_info('{table_name}')"))
|
||||
for safe_k in safe_keys:
|
||||
if safe_k not in existing_cols:
|
||||
cursor.execute(f'ALTER TABLE "{table_name}" ADD COLUMN "{safe_k}" TEXT')
|
||||
|
||||
# Insert data
|
||||
placeholders = ", ".join(["?"] * (1 + len(safe_keys))) # 1 for timestamp
|
||||
cols = ", ".join(["timestamp"] + list(safe_keys))
|
||||
values = [timestamp] + [
|
||||
json.dumps(data[k]) if isinstance(data[k], (dict, list)) else str(data[k]) for k in raw_keys
|
||||
]
|
||||
|
||||
cursor.execute(f'INSERT INTO "{table_name}" ({cols}) VALUES ({placeholders})', values)
|
||||
db.commit()
|
||||
|
||||
return jsonify({"status": "ok", "table": table_name}), 200
|
||||
|
||||
@app.route('/api/data/<host>')
|
||||
def serve_host_data(host) -> Any:
|
||||
db_path = get_db_path(host)
|
||||
cutoff = int(time.time()) - ROLLING_WINDOW_SECONDS
|
||||
table_name = f"{host.replace('-', '_')}"
|
||||
|
||||
with sqlite3.connect(db_path) as db:
|
||||
cursor = db.cursor()
|
||||
try:
|
||||
cur = cursor.execute(
|
||||
f"SELECT timestamp, * FROM '{table_name}' WHERE timestamp > ? ORDER BY timestamp ASC",
|
||||
(cutoff,)
|
||||
)
|
||||
cols = [desc[0] for desc in cur.description]
|
||||
entries = [dict(zip(cols, row)) for row in cur.fetchall()]
|
||||
except sqlite3.OperationalError:
|
||||
return jsonify([]) # Empty if table doesn't exist yet
|
||||
|
||||
return jsonify(entries)
|
||||
|
||||
@app.route('/db/<host>')
|
||||
def serve_sqlite_copy(host):
|
||||
db_path = get_db_path(host)
|
||||
if not os.path.exists(db_path):
|
||||
return jsonify({"error": "No DB yet for this host"}), 404
|
||||
|
||||
if os.path.getsize(db_path) < 3_000_000: # ~3MB max
|
||||
return send_file(db_path, mimetype='application/octet-stream')
|
||||
return jsonify({"error": "DB too large"}), 413
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(host="0.0.0.0", port=7331)
|
Loading…
Add table
Add a link
Reference in a new issue