Compare commits
4 Commits
465efbeb0e
...
5162a71be4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5162a71be4 | ||
|
|
0d3de9dc48 | ||
|
|
2a68ffcb90 | ||
|
|
10dc27a2c4 |
@@ -33,6 +33,7 @@ local function loadConfig()
|
||||
if cfg.serverUrl then SERVER_URL = cfg.serverUrl end
|
||||
if cfg.pollInterval then POLL_INTERVAL = cfg.pollInterval end
|
||||
if cfg.stateInterval then STATE_INTERVAL = cfg.stateInterval end
|
||||
if cfg.apiKey then API_KEY = cfg.apiKey end
|
||||
print("[CONFIG] Loaded from " .. CONFIG_FILE)
|
||||
end
|
||||
end
|
||||
@@ -46,6 +47,7 @@ local latestState = nil -- last broadcast from master
|
||||
local modem = nil
|
||||
local modemName = nil
|
||||
local running = true
|
||||
local API_KEY = nil -- optional API key for server auth
|
||||
|
||||
-------------------------------------------------
|
||||
-- Find modem
|
||||
@@ -71,6 +73,7 @@ local function httpPost(path, body)
|
||||
local url = SERVER_URL .. path
|
||||
local data = textutils.serialiseJSON(body)
|
||||
local headers = { ["Content-Type"] = "application/json" }
|
||||
if API_KEY then headers["Authorization"] = "Bearer " .. API_KEY end
|
||||
|
||||
local ok, err = pcall(function()
|
||||
local response = http.post(url, data, headers)
|
||||
@@ -89,8 +92,10 @@ end
|
||||
|
||||
local function httpGet(path)
|
||||
local url = SERVER_URL .. path
|
||||
local headers = nil
|
||||
if API_KEY then headers = { ["Authorization"] = "Bearer " .. API_KEY } end
|
||||
local ok, result = pcall(function()
|
||||
local response = http.get(url)
|
||||
local response = http.get(url, headers)
|
||||
if response then
|
||||
local data = response.readAll()
|
||||
response.close()
|
||||
@@ -274,6 +279,11 @@ local function main()
|
||||
print("[OK] Server URL: " .. SERVER_URL)
|
||||
print("[OK] Poll interval: " .. POLL_INTERVAL .. "s")
|
||||
print("[OK] State interval: " .. STATE_INTERVAL .. "s")
|
||||
if API_KEY then
|
||||
print("[OK] API key configured")
|
||||
else
|
||||
print("[WARN] No API key set (open access)")
|
||||
end
|
||||
print("")
|
||||
print("Bridge is running. Press Ctrl+T to stop.")
|
||||
print("Listening for master broadcasts on ch " .. BROADCAST_CHANNEL)
|
||||
|
||||
@@ -9,6 +9,10 @@ RUN npm install
|
||||
|
||||
COPY . .
|
||||
|
||||
# Accept API key at build time so Vite can inline it
|
||||
ARG VITE_API_KEY=""
|
||||
ENV VITE_API_KEY=${VITE_API_KEY}
|
||||
|
||||
RUN npm run build
|
||||
|
||||
# Stage 2: Serve with nginx
|
||||
|
||||
@@ -1,12 +1,24 @@
|
||||
import { create } from 'zustand';
|
||||
|
||||
const WS_URL = import.meta.env.VITE_WS_URL ||
|
||||
const _baseWsUrl = import.meta.env.VITE_WS_URL ||
|
||||
`${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${window.location.host}/ws`;
|
||||
const API_URL = import.meta.env.VITE_API_URL ||
|
||||
`${window.location.protocol}//${window.location.host}/api`;
|
||||
const API_KEY = import.meta.env.VITE_API_KEY || '';
|
||||
|
||||
console.log('🔌 WebSocket URL:', WS_URL);
|
||||
// Append API key to WebSocket URL as query param
|
||||
const WS_URL = API_KEY ? `${_baseWsUrl}?key=${encodeURIComponent(API_KEY)}` : _baseWsUrl;
|
||||
|
||||
// Build common headers for authenticated requests
|
||||
function authHeaders(extra = {}) {
|
||||
const headers = { 'Content-Type': 'application/json', ...extra };
|
||||
if (API_KEY) headers['Authorization'] = `Bearer ${API_KEY}`;
|
||||
return headers;
|
||||
}
|
||||
|
||||
console.log('🔌 WebSocket URL:', _baseWsUrl);
|
||||
console.log('📡 API URL:', API_URL);
|
||||
if (API_KEY) console.log('🔒 API key configured');
|
||||
|
||||
// Generate a unique command ID for idempotent requests
|
||||
function newCommandId() {
|
||||
@@ -187,7 +199,7 @@ export const useInventoryStore = create((set, get) => ({
|
||||
try {
|
||||
const response = await fetch(`${API_URL}/order`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
headers: authHeaders(),
|
||||
body: JSON.stringify({ itemName, amount, dropperName, commandId: newCommandId() }),
|
||||
});
|
||||
return await response.json();
|
||||
@@ -201,7 +213,7 @@ export const useInventoryStore = create((set, get) => ({
|
||||
try {
|
||||
const response = await fetch(`${API_URL}/scan`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
headers: authHeaders(),
|
||||
body: JSON.stringify({ commandId: newCommandId() }),
|
||||
});
|
||||
return await response.json();
|
||||
@@ -215,7 +227,7 @@ export const useInventoryStore = create((set, get) => ({
|
||||
try {
|
||||
const response = await fetch(`${API_URL}/smelting/toggle`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
headers: authHeaders(),
|
||||
body: JSON.stringify({ commandId: newCommandId() }),
|
||||
});
|
||||
return await response.json();
|
||||
@@ -229,7 +241,7 @@ export const useInventoryStore = create((set, get) => ({
|
||||
try {
|
||||
const response = await fetch(`${API_URL}/recipes/toggle`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
headers: authHeaders(),
|
||||
body: JSON.stringify({ recipe, commandId: newCommandId() }),
|
||||
});
|
||||
return await response.json();
|
||||
@@ -243,7 +255,7 @@ export const useInventoryStore = create((set, get) => ({
|
||||
try {
|
||||
await fetch(`${API_URL}/recipes/enable-all`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
headers: authHeaders(),
|
||||
body: JSON.stringify({ commandId: newCommandId() }),
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -255,7 +267,7 @@ export const useInventoryStore = create((set, get) => ({
|
||||
try {
|
||||
await fetch(`${API_URL}/recipes/disable-all`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
headers: authHeaders(),
|
||||
body: JSON.stringify({ commandId: newCommandId() }),
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -267,7 +279,7 @@ export const useInventoryStore = create((set, get) => ({
|
||||
try {
|
||||
const response = await fetch(`${API_URL}/craft`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
headers: authHeaders(),
|
||||
body: JSON.stringify({ recipeIdx, commandId: newCommandId() }),
|
||||
});
|
||||
return await response.json();
|
||||
@@ -281,7 +293,7 @@ export const useInventoryStore = create((set, get) => ({
|
||||
try {
|
||||
const response = await fetch(`${API_URL}/sort-barrel`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
headers: authHeaders(),
|
||||
body: JSON.stringify({ barrelName, commandId: newCommandId() }),
|
||||
});
|
||||
return await response.json();
|
||||
@@ -296,7 +308,7 @@ export const useInventoryStore = create((set, get) => ({
|
||||
try {
|
||||
const response = await fetch(`${API_URL}/dropper-nicknames`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
headers: authHeaders(),
|
||||
body: JSON.stringify({ dropperName, nickname }),
|
||||
});
|
||||
const result = await response.json();
|
||||
|
||||
@@ -5,6 +5,8 @@ services:
|
||||
- inventory-network
|
||||
volumes:
|
||||
- server-data:/data
|
||||
environment:
|
||||
- API_KEY=${API_KEY:-}
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD", "node", "-e", "require('http').get('http://localhost:3001/api/health',r=>{process.exit(r.statusCode===200?0:1)}).on('error',()=>process.exit(1))"]
|
||||
@@ -14,7 +16,10 @@ services:
|
||||
retries: 3
|
||||
|
||||
client:
|
||||
build: ./client
|
||||
build:
|
||||
context: ./client
|
||||
args:
|
||||
VITE_API_KEY: ${API_KEY:-}
|
||||
ports:
|
||||
- "80:80"
|
||||
networks:
|
||||
|
||||
Reference in New Issue
Block a user