Upload files to "routes"
This commit is contained in:
104
routes/api.py
Normal file
104
routes/api.py
Normal file
@@ -0,0 +1,104 @@
|
||||
from flask import Blueprint, request, jsonify
|
||||
from db import get_db
|
||||
import json
|
||||
from datetime import datetime
|
||||
|
||||
api = Blueprint('api', __name__)
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# POST /api/state
|
||||
# Tampermonkey pushes a full town snapshot every poll cycle.
|
||||
# ------------------------------------------------------------------
|
||||
@api.route('/api/state', methods=['POST'])
|
||||
def receive_state():
|
||||
data = request.get_json(silent=True)
|
||||
if not data:
|
||||
return jsonify({'error': 'no data'}), 400
|
||||
|
||||
towns = data.get('towns', [])
|
||||
player = data.get('player', '')
|
||||
world_id = data.get('world_id', '')
|
||||
|
||||
conn = get_db()
|
||||
c = conn.cursor()
|
||||
for town in towns:
|
||||
c.execute('''
|
||||
INSERT INTO town_state (town_id, town_name, player, world_id, data, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT(town_id) DO UPDATE SET
|
||||
town_name = excluded.town_name,
|
||||
player = excluded.player,
|
||||
world_id = excluded.world_id,
|
||||
data = excluded.data,
|
||||
updated_at = excluded.updated_at
|
||||
''', (
|
||||
str(town['town_id']),
|
||||
town.get('town_name', ''),
|
||||
player,
|
||||
world_id,
|
||||
json.dumps(town),
|
||||
datetime.utcnow().isoformat()
|
||||
))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return jsonify({'ok': True, 'towns_updated': len(towns)})
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# GET /api/commands/pending
|
||||
# Tampermonkey polls this to get the next command to execute.
|
||||
# Returns ONE command at a time, marks it as 'executing'.
|
||||
# ------------------------------------------------------------------
|
||||
@api.route('/api/commands/pending', methods=['GET'])
|
||||
def get_pending_command():
|
||||
conn = get_db()
|
||||
c = conn.cursor()
|
||||
row = c.execute('''
|
||||
SELECT * FROM commands
|
||||
WHERE status = 'pending'
|
||||
ORDER BY id ASC
|
||||
LIMIT 1
|
||||
''').fetchone()
|
||||
|
||||
if not row:
|
||||
conn.close()
|
||||
return jsonify({'command': None})
|
||||
|
||||
c.execute('''
|
||||
UPDATE commands
|
||||
SET status = 'executing', updated_at = ?
|
||||
WHERE id = ?
|
||||
''', (datetime.utcnow().isoformat(), row['id']))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
return jsonify({
|
||||
'command': {
|
||||
'id': row['id'],
|
||||
'town_id': row['town_id'],
|
||||
'type': row['type'],
|
||||
'payload': json.loads(row['payload'])
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# POST /api/commands/<id>/result
|
||||
# Tampermonkey reports back whether the command succeeded or failed.
|
||||
# ------------------------------------------------------------------
|
||||
@api.route('/api/commands/<int:cmd_id>/result', methods=['POST'])
|
||||
def command_result(cmd_id):
|
||||
data = request.get_json(silent=True) or {}
|
||||
status = data.get('status', 'done') # 'done' | 'failed'
|
||||
msg = data.get('message', '')
|
||||
|
||||
conn = get_db()
|
||||
conn.execute('''
|
||||
UPDATE commands
|
||||
SET status = ?, result_msg = ?, updated_at = ?
|
||||
WHERE id = ?
|
||||
''', (status, msg, datetime.utcnow().isoformat(), cmd_id))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return jsonify({'ok': True})
|
||||
127
routes/dashboard.py
Normal file
127
routes/dashboard.py
Normal file
@@ -0,0 +1,127 @@
|
||||
from flask import Blueprint, render_template, request, jsonify
|
||||
from db import get_db
|
||||
import json
|
||||
from datetime import datetime
|
||||
|
||||
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, world_id, 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'],
|
||||
'world_id': row['world_id'],
|
||||
'updated_at': row['updated_at'],
|
||||
'resources': {
|
||||
'wood': d.get('wood', 0),
|
||||
'stone': d.get('stone', 0),
|
||||
'iron': d.get('iron', 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', []),
|
||||
})
|
||||
return jsonify(towns)
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# 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
|
||||
|
||||
conn = get_db()
|
||||
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>
|
||||
# Cancel a pending command from the dashboard.
|
||||
# ------------------------------------------------------------------
|
||||
@dashboard.route('/dashboard/commands/<int:cmd_id>', methods=['DELETE'])
|
||||
def cancel_command(cmd_id):
|
||||
conn = get_db()
|
||||
conn.execute('''
|
||||
UPDATE commands SET status = 'cancelled', updated_at = ?
|
||||
WHERE id = ? AND status = 'pending'
|
||||
''', (datetime.utcnow().isoformat(), cmd_id))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return jsonify({'ok': True})
|
||||
Reference in New Issue
Block a user