Add Cross-Project Integration API for RemoteTurtle system
This commit is contained in:
@@ -966,6 +966,78 @@ wss.on('close', () => {
|
||||
clearInterval(WS_PING_INTERVAL);
|
||||
});
|
||||
|
||||
// ========== Cross-Project Integration API ==========
|
||||
// These endpoints allow the RemoteTurtle system to query inventory state
|
||||
|
||||
const TURTLE_SERVER_URL = process.env.TURTLE_SERVER_URL || ''; // e.g. http://turtle-server:3001
|
||||
|
||||
// Find where a specific item is stored (for turtles to pick up items)
|
||||
app.get('/api/integration/locate-item', (req, res) => {
|
||||
const { name, minCount } = req.query;
|
||||
if (!name) return res.status(400).json({ error: 'Item name required (?name=minecraft:diamond)' });
|
||||
|
||||
const items = inventoryState.itemList || [];
|
||||
const match = items.find(i => i.name === name);
|
||||
if (!match || match.count < (parseInt(minCount) || 1)) {
|
||||
return res.json({ found: false, available: match ? match.count : 0 });
|
||||
}
|
||||
res.json({ found: true, name: match.name, count: match.count, displayName: match.displayName });
|
||||
});
|
||||
|
||||
// Search items by partial name (for turtle autocomplete/fuzzy matching)
|
||||
app.get('/api/integration/search-items', (req, res) => {
|
||||
const { q, limit } = req.query;
|
||||
if (!q) return res.status(400).json({ error: 'Search query required (?q=diamond)' });
|
||||
|
||||
const items = inventoryState.itemList || [];
|
||||
const query = q.toLowerCase();
|
||||
const results = items
|
||||
.filter(i => i.name.toLowerCase().includes(query) || (i.displayName || '').toLowerCase().includes(query))
|
||||
.sort((a, b) => b.count - a.count)
|
||||
.slice(0, parseInt(limit) || 20);
|
||||
|
||||
res.json({ results });
|
||||
});
|
||||
|
||||
// Get storage summary (available space, total items) for turtle decision-making
|
||||
app.get('/api/integration/storage-status', (req, res) => {
|
||||
res.json({
|
||||
grandTotal: inventoryState.grandTotal || 0,
|
||||
chestCount: inventoryState.chestCount || 0,
|
||||
totalSlots: inventoryState.totalSlots || 0,
|
||||
usedSlots: inventoryState.usedSlots || 0,
|
||||
freeSlots: (inventoryState.totalSlots || 0) - (inventoryState.usedSlots || 0),
|
||||
lastUpdate,
|
||||
bridgeConnected: bridgeClients.size > 0,
|
||||
});
|
||||
});
|
||||
|
||||
// Get full item list (for turtle dump target selection)
|
||||
app.get('/api/integration/items', (req, res) => {
|
||||
const items = inventoryState.itemList || [];
|
||||
res.json({ items: items.map(i => ({ name: i.name, count: i.count })) });
|
||||
});
|
||||
|
||||
// Get alerts (so turtles know what items are running low)
|
||||
app.get('/api/integration/low-stock', (req, res) => {
|
||||
const triggered = (alertsState || []).filter(a => a.triggered !== false);
|
||||
res.json({ alerts: triggered });
|
||||
});
|
||||
|
||||
// Proxy to turtle server for combined dashboard info
|
||||
app.get('/api/integration/turtle-status', async (req, res) => {
|
||||
if (!TURTLE_SERVER_URL) {
|
||||
return res.json({ configured: false, message: 'TURTLE_SERVER_URL not configured' });
|
||||
}
|
||||
try {
|
||||
const resp = await fetch(`${TURTLE_SERVER_URL}/api/turtles`);
|
||||
const data = await resp.json();
|
||||
res.json({ configured: true, ...data });
|
||||
} catch (err) {
|
||||
res.status(502).json({ configured: true, error: `Cannot reach turtle server: ${err.message}` });
|
||||
}
|
||||
});
|
||||
|
||||
// ========== Start Server ==========
|
||||
|
||||
server.listen(PORT, HOST, () => {
|
||||
@@ -973,6 +1045,9 @@ server.listen(PORT, HOST, () => {
|
||||
console.log(`\nBridge HTTP endpoint: http://localhost:${PORT}/api/bridge/state`);
|
||||
console.log(`Bridge WebSocket: ws://localhost:${PORT}/ws/bridge`);
|
||||
console.log(`Web client WebSocket: ws://localhost:${PORT}/ws`);
|
||||
if (TURTLE_SERVER_URL) {
|
||||
console.log(`🐢 Turtle server integration: ${TURTLE_SERVER_URL}`);
|
||||
}
|
||||
});
|
||||
|
||||
// Graceful shutdown
|
||||
|
||||
Reference in New Issue
Block a user