Files
grepo-remote/routes/dashboard.py
2026-04-20 23:59:01 +03:00

203 lines
7.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from flask import Blueprint, render_template, request, jsonify
from db import get_db
import json
from datetime import datetime, timedelta
dashboard = Blueprint('dashboard', __name__)
# ------------------------------------------------------------------
# GET /
# Serve the dashboard HTML
# ------------------------------------------------------------------
@dashboard.route('/')
def index():
return render_template('dashboard.html')
# ------------------------------------------------------------------
# GET /dashboard/towns
# Returns all known towns with their latest state snapshot.
# ------------------------------------------------------------------
@dashboard.route('/dashboard/towns', methods=['GET'])
def get_towns():
conn = get_db()
rows = conn.execute('''
SELECT town_id, town_name, player, player_id, alliance_id,
world_id, x, y, sea, data, updated_at
FROM town_state
ORDER BY town_name ASC
''').fetchall()
conn.close()
towns = []
for row in rows:
d = json.loads(row['data'])
towns.append({
'town_id': row['town_id'],
'town_name': row['town_name'],
'player': row['player'],
'player_id': row['player_id'],
'alliance_id': row['alliance_id'],
'world_id': row['world_id'],
'x': row['x'],
'y': row['y'],
'sea': row['sea'],
'updated_at': row['updated_at'],
'resources': {
'wood': d.get('wood', 0),
'stone': d.get('stone', 0),
'iron': d.get('iron', 0),
'storage': d.get('storage', 0),
'population': d.get('population', 0),
},
'buildings': d.get('buildings', {}),
'units': d.get('units', {}),
'points': d.get('points', 0),
'god': d.get('god', None),
'build_queue': d.get('buildingOrder', []),
'build_data': d.get('buildData', {}),
'unit_data': d.get('unitData', {}),
'researches': d.get('researches', {}),
'has_premium': d.get('has_premium', False),
'bonuses': d.get('bonuses', {}),
'wonder_points': d.get('wonder_points', 0),
'total_points': d.get('total_points', 0),
})
return jsonify(towns)
# ------------------------------------------------------------------
# GET /dashboard/client-status
# Returns whether the Tampermonkey client is considered online.
# Online = at least one town_state row updated within the last 150 s.
# ------------------------------------------------------------------
@dashboard.route('/dashboard/client-status', methods=['GET'])
def client_status():
conn = get_db()
row = conn.execute('''
SELECT MAX(updated_at) AS last_seen FROM town_state
''').fetchone()
conn.close()
last_seen = row['last_seen'] if row else None
if last_seen:
try:
dt = datetime.fromisoformat(last_seen)
age_s = (datetime.utcnow() - dt).total_seconds()
online = age_s <= 150
except Exception:
online = False
else:
online = False
return jsonify({'online': online, 'last_seen': last_seen})
# ------------------------------------------------------------------
# GET /dashboard/commands
# Returns command history (last 50) for the log panel.
# ------------------------------------------------------------------
@dashboard.route('/dashboard/commands', methods=['GET'])
def get_commands():
conn = get_db()
rows = conn.execute('''
SELECT id, town_id, town_name, type, payload, status, result_msg, created_at, updated_at
FROM commands
ORDER BY id DESC
LIMIT 50
''').fetchall()
conn.close()
return jsonify([dict(r) for r in rows])
# ------------------------------------------------------------------
# POST /dashboard/commands
# Dashboard sends a new command.
# Body: { town_id, town_name, type: 'build'|'recruit', payload: {...} }
# ------------------------------------------------------------------
@dashboard.route('/dashboard/commands', methods=['POST'])
def create_command():
data = request.get_json(silent=True)
if not data:
return jsonify({'error': 'no data'}), 400
required = ['town_id', 'type', 'payload']
for field in required:
if field not in data:
return jsonify({'error': f'missing field: {field}'}), 400
cmd_type = data['type']
if cmd_type not in ('build', 'recruit'):
return jsonify({'error': 'type must be build or recruit'}), 400
# Reject if the Tampermonkey client is offline (no state push in last 150 s)
conn = get_db()
row = conn.execute('SELECT MAX(updated_at) AS last_seen FROM town_state').fetchone()
last_seen = row['last_seen'] if row else None
client_online = False
if last_seen:
try:
dt = datetime.fromisoformat(last_seen)
client_online = (datetime.utcnow() - dt).total_seconds() <= 150
except Exception:
pass
if not client_online:
conn.close()
return jsonify({'error': 'client_offline', 'message': 'Το script είναι offline — δεν μπορείτε να στείλετε εντολές.'}), 503
c = conn.cursor()
c.execute('''
INSERT INTO commands (town_id, town_name, type, payload, status, created_at, updated_at)
VALUES (?, ?, ?, ?, 'pending', ?, ?)
''', (
str(data['town_id']),
data.get('town_name', ''),
cmd_type,
json.dumps(data['payload']),
datetime.utcnow().isoformat(),
datetime.utcnow().isoformat()
))
cmd_id = c.lastrowid
conn.commit()
conn.close()
return jsonify({'ok': True, 'id': cmd_id})
# ------------------------------------------------------------------
# DELETE /dashboard/commands/<id>
# Fully delete a command from the dashboard history.
# ------------------------------------------------------------------
@dashboard.route('/dashboard/commands/<int:cmd_id>', methods=['DELETE'])
def cancel_command(cmd_id):
conn = get_db()
conn.execute('DELETE FROM commands WHERE id = ?', (cmd_id,))
conn.commit()
conn.close()
return jsonify({'ok': True})
# ------------------------------------------------------------------
# POST /dashboard/commands/fail-stale
# Mark all pending/executing commands as failed (called by dashboard
# when it detects the client has gone offline).
# ------------------------------------------------------------------
@dashboard.route('/dashboard/commands/fail-stale', methods=['POST'])
def fail_stale_commands():
conn = get_db()
c = conn.cursor()
c.execute('''
UPDATE commands
SET status = 'failed',
result_msg = 'Client went offline',
updated_at = ?
WHERE status IN ('pending', 'executing')
''', (datetime.utcnow().isoformat(),))
affected = c.rowcount
conn.commit()
conn.close()
return jsonify({'ok': True, 'failed': affected})