mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-04-10 13:02:59 +00:00
more robust first time install script, ensure it creates config_secrets
This commit is contained in:
@@ -233,6 +233,37 @@ else
|
||||
fi
|
||||
echo ""
|
||||
|
||||
CURRENT_STEP="Ensure secrets configuration exists"
|
||||
echo "Step 3.1: Ensuring secrets configuration exists..."
|
||||
echo "-----------------------------------------------"
|
||||
|
||||
# Ensure config directory exists
|
||||
mkdir -p "$PROJECT_ROOT_DIR/config"
|
||||
|
||||
# Create config_secrets.json from template if missing
|
||||
if [ ! -f "$PROJECT_ROOT_DIR/config/config_secrets.json" ]; then
|
||||
if [ -f "$PROJECT_ROOT_DIR/config/config_secrets.template.json" ]; then
|
||||
echo "Creating config/config_secrets.json from template..."
|
||||
cp "$PROJECT_ROOT_DIR/config/config_secrets.template.json" "$PROJECT_ROOT_DIR/config/config_secrets.json"
|
||||
chmod 600 "$PROJECT_ROOT_DIR/config/config_secrets.json"
|
||||
echo "✓ Secrets file created from template"
|
||||
else
|
||||
echo "⚠ Template config/config_secrets.template.json not found; creating a minimal secrets file"
|
||||
cat > "$PROJECT_ROOT_DIR/config/config_secrets.json" <<'EOF'
|
||||
{
|
||||
"weather": {
|
||||
"api_key": "YOUR_OPENWEATHERMAP_API_KEY"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
chmod 600 "$PROJECT_ROOT_DIR/config/config_secrets.json"
|
||||
echo "✓ Minimal secrets file created"
|
||||
fi
|
||||
else
|
||||
echo "Secrets file already exists; leaving as-is"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
CURRENT_STEP="Install project Python dependencies"
|
||||
echo "Step 4: Installing Python project dependencies..."
|
||||
echo "-----------------------------------------------"
|
||||
@@ -413,6 +444,12 @@ if [ -f "$PROJECT_ROOT_DIR/config/config.json" ]; then
|
||||
echo "✓ Config file permissions set"
|
||||
fi
|
||||
|
||||
# Set proper permissions for secrets file (restrictive)
|
||||
if [ -f "$PROJECT_ROOT_DIR/config/config_secrets.json" ]; then
|
||||
chmod 600 "$PROJECT_ROOT_DIR/config/config_secrets.json"
|
||||
echo "✓ Secrets file permissions set"
|
||||
fi
|
||||
|
||||
echo "✓ File ownership configured"
|
||||
echo ""
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ Wants=ledmatrix.service
|
||||
Type=simple
|
||||
User=root
|
||||
WorkingDirectory=/home/ledpi/LEDMatrix
|
||||
Environment=USE_THREADING=1
|
||||
ExecStart=/usr/bin/python3 /home/ledpi/LEDMatrix/start_web_conditionally.py
|
||||
Restart=on-failure
|
||||
RestartSec=10
|
||||
|
||||
@@ -58,6 +58,7 @@ def main():
|
||||
# This is important for systemd to correctly manage the web server process.
|
||||
# Ensure PYTHONPATH is set correctly if web_interface.py has relative imports to src
|
||||
# The WorkingDirectory in systemd service should handle this for web_interface.py
|
||||
print(f"Launching web interface: {sys.executable} {WEB_INTERFACE_SCRIPT} (USE_THREADING={os.getenv('USE_THREADING','0')}, FORCE_THREADING={os.getenv('FORCE_THREADING','0')})")
|
||||
os.execvp(sys.executable, [sys.executable, WEB_INTERFACE_SCRIPT])
|
||||
except Exception as e:
|
||||
print(f"Failed to exec web interface: {e}")
|
||||
|
||||
@@ -692,17 +692,17 @@
|
||||
<i class="fas fa-{{ 'play' if system_status.service_active else 'stop' }}"></i>
|
||||
Service {{ 'Active' if system_status.service_active else 'Inactive' }}
|
||||
</div>
|
||||
<div class="status-item {{ 'status-warning' if system_status.cpu_percent > 80 else 'status-active' }}">
|
||||
<div class="status-item {{ 'status-warning' if system_status and system_status.cpu_percent and system_status.cpu_percent > 80 else 'status-active' }}">
|
||||
<i class="fas fa-microchip"></i>
|
||||
{{ system_status.cpu_percent }}% CPU
|
||||
{{ system_status.cpu_percent if system_status and system_status.cpu_percent is defined else 0 }}% CPU
|
||||
</div>
|
||||
<div class="status-item {{ 'status-warning' if system_status.memory_used_percent > 80 else 'status-active' }}">
|
||||
<div class="status-item {{ 'status-warning' if system_status and system_status.memory_used_percent and system_status.memory_used_percent > 80 else 'status-active' }}">
|
||||
<i class="fas fa-memory"></i>
|
||||
{{ system_status.memory_used_percent }}% RAM
|
||||
{{ system_status.memory_used_percent if system_status and system_status.memory_used_percent is defined else 0 }}% RAM
|
||||
</div>
|
||||
<div class="status-item {{ 'status-warning' if system_status.cpu_temp > 70 else 'status-active' }}">
|
||||
<div class="status-item {{ 'status-warning' if system_status and system_status.cpu_temp and system_status.cpu_temp > 70 else 'status-active' }}">
|
||||
<i class="fas fa-thermometer-half"></i>
|
||||
{{ system_status.cpu_temp }}°C
|
||||
{{ system_status.cpu_temp if system_status and system_status.cpu_temp is defined else 0 }}°C
|
||||
</div>
|
||||
<div class="status-item status-active">
|
||||
<i class="fas fa-clock"></i>
|
||||
@@ -858,15 +858,15 @@
|
||||
<h3>System Overview</h3>
|
||||
<div class="stats-grid">
|
||||
<div class="stat-card">
|
||||
<div class="stat-value">{{ system_status.cpu_percent }}%</div>
|
||||
<div class="stat-value">{{ system_status.cpu_percent if system_status and system_status.cpu_percent is defined else 0 }}%</div>
|
||||
<div class="stat-label">CPU Usage</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-value">{{ system_status.memory_used_percent }}%</div>
|
||||
<div class="stat-value">{{ system_status.memory_used_percent if system_status and system_status.memory_used_percent is defined else 0 }}%</div>
|
||||
<div class="stat-label">Memory Usage</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-value">{{ system_status.cpu_temp }}°C</div>
|
||||
<div class="stat-value">{{ system_status.cpu_temp if system_status and system_status.cpu_temp is defined else 0 }}°C</div>
|
||||
<div class="stat-label">CPU Temperature</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
@@ -878,7 +878,7 @@
|
||||
<div class="stat-label">Resolution</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-value">{{ system_status.disk_used_percent }}%</div>
|
||||
<div class="stat-value">{{ system_status.disk_used_percent if system_status and system_status.disk_used_percent is defined else 0 }}%</div>
|
||||
<div class="stat-label">Disk Usage</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1630,7 +1630,7 @@
|
||||
<h4>Weather API</h4>
|
||||
<div class="form-group">
|
||||
<label for="weather_api_key">OpenWeatherMap API Key:</label>
|
||||
<input type="password" class="form-control" id="weather_api_key" name="weather_api_key" value="{{ secrets_config.weather.api_key if secrets_config.weather.api_key != 'YOUR_OPENWEATHERMAP_API_KEY' else '' }}" placeholder="Enter your OpenWeatherMap API key">
|
||||
<input type="password" class="form-control" id="weather_api_key" name="weather_api_key" value="{{ (secrets_config.weather.api_key if secrets_config and secrets_config.weather and secrets_config.weather.api_key != 'YOUR_OPENWEATHERMAP_API_KEY' else '') if secrets_config else '' }}" placeholder="Enter your OpenWeatherMap API key">
|
||||
<div class="description">Get your free API key from <a href="https://openweathermap.org/api" target="_blank">OpenWeatherMap</a></div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -38,12 +38,16 @@ import logging
|
||||
|
||||
app = Flask(__name__)
|
||||
app.secret_key = os.urandom(24)
|
||||
# Prefer eventlet when available to avoid Werkzeug dev server quirks
|
||||
try:
|
||||
import eventlet # noqa: F401
|
||||
ASYNC_MODE = 'eventlet'
|
||||
except Exception:
|
||||
# Prefer eventlet when available, but allow forcing threading via env for troubleshooting
|
||||
force_threading = os.getenv('USE_THREADING', '0') == '1' or os.getenv('FORCE_THREADING', '0') == '1'
|
||||
if force_threading:
|
||||
ASYNC_MODE = 'threading'
|
||||
else:
|
||||
try:
|
||||
import eventlet # noqa: F401
|
||||
ASYNC_MODE = 'eventlet'
|
||||
except Exception:
|
||||
ASYNC_MODE = 'threading'
|
||||
|
||||
socketio = SocketIO(app, cors_allowed_origins="*", async_mode=ASYNC_MODE)
|
||||
|
||||
@@ -382,6 +386,16 @@ def index():
|
||||
# Get raw config data for JSON editors
|
||||
main_config_data = config_manager.get_raw_file_content('main')
|
||||
secrets_config_data = config_manager.get_raw_file_content('secrets')
|
||||
# Normalize secrets structure for template safety
|
||||
try:
|
||||
if not isinstance(secrets_config_data, dict):
|
||||
secrets_config_data = {}
|
||||
if 'weather' not in secrets_config_data or not isinstance(secrets_config_data['weather'], dict):
|
||||
secrets_config_data['weather'] = {}
|
||||
if 'api_key' not in secrets_config_data['weather']:
|
||||
secrets_config_data['weather']['api_key'] = ''
|
||||
except Exception:
|
||||
secrets_config_data = {'weather': {'api_key': ''}}
|
||||
main_config_json = json.dumps(main_config_data, indent=4)
|
||||
secrets_config_json = json.dumps(secrets_config_data, indent=4)
|
||||
|
||||
@@ -398,18 +412,20 @@ def index():
|
||||
editor_mode=editor_mode)
|
||||
|
||||
except Exception as e:
|
||||
# Return a minimal, valid response to avoid Werkzeug assertion errors
|
||||
# Return a minimal, valid response to avoid template errors when keys are missing
|
||||
logger.error(f"Error loading configuration on index: {e}", exc_info=True)
|
||||
safe_system_status = get_system_status()
|
||||
safe_secrets = {'weather': {'api_key': ''}}
|
||||
return render_template('index_v2.html',
|
||||
schedule_config={},
|
||||
main_config={},
|
||||
main_config_data={},
|
||||
secrets_config={},
|
||||
secrets_config=safe_secrets,
|
||||
main_config_json="{}",
|
||||
secrets_config_json="{}",
|
||||
main_config_path="",
|
||||
secrets_config_path="",
|
||||
system_status={'error': str(e)},
|
||||
system_status=safe_system_status,
|
||||
editor_mode=False)
|
||||
|
||||
def get_system_status():
|
||||
@@ -1377,4 +1393,5 @@ if __name__ == '__main__':
|
||||
# Run the app
|
||||
# In threading mode this uses Werkzeug; allow it explicitly for systemd usage
|
||||
# Use eventlet server when available; fall back to Werkzeug in threading mode
|
||||
logger.info(f"Starting web interface on http://0.0.0.0:5001 (async_mode={ASYNC_MODE})")
|
||||
socketio.run(app, host='0.0.0.0', port=5001, debug=False, use_reloader=False)
|
||||
Reference in New Issue
Block a user