Skip to content

Commit 97a4e83

Browse files
committed
Added configuration management via UI (Settings page) using config.json
1 parent ee32054 commit 97a4e83

4 files changed

Lines changed: 171 additions & 7 deletions

File tree

app.py

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,21 @@
99
app = Flask(__name__, static_folder='assets')
1010
DB_FILE = "database.json"
1111
GITHUB_RAW_URL = "https://raw.githubusercontent.com/Princeddn/chirp-api/data-backup/database.json"
12-
CHIRPSTACK_API_URL = os.getenv("CHIRPSTACK_API_URL", "https://chirpstack.example.com")
13-
CHIRPSTACK_API_TOKEN = os.getenv("CHIRPSTACK_API_TOKEN", "your_token_here")
12+
CONFIG_FILE = "config.json"
13+
14+
def load_config():
15+
try:
16+
if os.path.exists(CONFIG_FILE):
17+
with open(CONFIG_FILE, "r", encoding="utf-8") as f:
18+
return json.load(f)
19+
except Exception as e:
20+
print(f"Error loading config: {e}")
21+
return {}
22+
23+
config = load_config()
24+
CHIRPSTACK_API_URL = config.get("CHIRPSTACK_API_URL", os.getenv("CHIRPSTACK_API_URL", "https://chirpstack.example.com"))
25+
CHIRPSTACK_API_TOKEN = config.get("CHIRPSTACK_API_TOKEN", os.getenv("CHIRPSTACK_API_TOKEN", "your_token_here"))
26+
1427

1528
def restore_database_from_github(force=False):
1629
if force or not os.path.exists(DB_FILE) or os.path.getsize(DB_FILE) == 0:
@@ -142,5 +155,29 @@ def send_downlink():
142155
except Exception as e:
143156
return jsonify({"error": str(e)}), 500
144157

158+
@app.route('/api/config', methods=['GET'])
159+
def get_config():
160+
# Return config but mask the token for security in UI if desired,
161+
# but for manual edit we might need to show it or a placeholder.
162+
# Here sending plain text for user convenience as requested.
163+
return jsonify(load_config())
164+
165+
@app.route('/api/config', methods=['POST'])
166+
def update_config():
167+
try:
168+
new_config = request.json
169+
# Basic validation could go here
170+
with open(CONFIG_FILE, "w", encoding="utf-8") as f:
171+
json.dump(new_config, f, indent=2)
172+
173+
# Update runtime globals
174+
global CHIRPSTACK_API_URL, CHIRPSTACK_API_TOKEN
175+
CHIRPSTACK_API_URL = new_config.get("CHIRPSTACK_API_URL", CHIRPSTACK_API_URL)
176+
CHIRPSTACK_API_TOKEN = new_config.get("CHIRPSTACK_API_TOKEN", CHIRPSTACK_API_TOKEN)
177+
178+
return jsonify({"status": "success", "message": "Configuration updated"})
179+
except Exception as e:
180+
return jsonify({"error": str(e)}), 500
181+
145182
if __name__ == '__main__':
146-
app.run(host='0.0.0.0', port=5000, debug=True)
183+
app.run(debug=True, port=3000)

assets/js/dashboard.js

Lines changed: 76 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ function switchView(viewName) {
2525
'devices': 'Gestion des Appareils',
2626
'console': 'Terminal de Commande',
2727
'analytics': 'Analyses & Rapports',
28-
'data': 'Historique des Données'
28+
'data': 'Historique des Données',
29+
'settings': 'Configuration'
2930
};
3031
document.getElementById('page-title').innerText = titles[viewName];
3132

@@ -523,7 +524,78 @@ function showAddDeviceModal() { alert("Fonctionnalité backend à venir."); }
523524

524525
// Auto-refresh logic for Live Feel
525526
setInterval(() => {
526-
// Only fetch if tab is active to save bandwidth?
527-
// Or always to keep up.
528527
fetchData();
529-
}, 30000);
528+
}, 30000);
529+
530+
// --- Settings Logic ---
531+
function togglePassword(id) {
532+
const el = document.getElementById(id);
533+
if (el) el.type = el.type === 'password' ? 'text' : 'password';
534+
}
535+
536+
async function loadConfig() {
537+
try {
538+
const res = await fetch('/api/config');
539+
if (!res.ok) return; // Silent fail if optional
540+
const config = await res.json();
541+
542+
if (document.getElementById('conf-chirpstack-url')) {
543+
document.getElementById('conf-chirpstack-url').value = config.CHIRPSTACK_API_URL || '';
544+
document.getElementById('conf-chirpstack-token').value = config.CHIRPSTACK_API_TOKEN || '';
545+
document.getElementById('conf-github-repo').value = config.GITHUB_REPO || '';
546+
document.getElementById('conf-github-branch').value = config.GITHUB_BRANCH || '';
547+
}
548+
} catch (e) {
549+
console.error("Failed to load config", e);
550+
}
551+
}
552+
553+
async function saveConfig() {
554+
const config = {
555+
CHIRPSTACK_API_URL: document.getElementById('conf-chirpstack-url').value,
556+
CHIRPSTACK_API_TOKEN: document.getElementById('conf-chirpstack-token').value,
557+
GITHUB_REPO: document.getElementById('conf-github-repo').value,
558+
GITHUB_BRANCH: document.getElementById('conf-github-branch').value
559+
};
560+
561+
try {
562+
const res = await fetch('/api/config', {
563+
method: 'POST',
564+
headers: { 'Content-Type': 'application/json' },
565+
body: JSON.stringify(config)
566+
});
567+
const data = await res.json();
568+
if (res.ok) alert("Configuration sauvegardée !");
569+
else alert("Erreur: " + data.error);
570+
} catch (e) {
571+
alert("Erreur réseau: " + e.message);
572+
}
573+
}
574+
575+
// Hook loadConfig into navigation or init
576+
// For simplicity, load it when View Switching to settings
577+
const originalSwitchView = window.switchView;
578+
window.switchView = function (viewName) {
579+
if (originalSwitchView) originalSwitchView(viewName); // Call original defined in global scope (actually it's defined as function switchView... hoisting might handle it but let's be safe)
580+
else switchViewRef(viewName); // Fallback if reassignment weirdness
581+
582+
if (viewName === 'settings') loadConfig();
583+
};
584+
// Re-assign original reference to be handled
585+
function switchViewRef(viewName) {
586+
document.querySelectorAll('.nav-item').forEach(btn => btn.classList.remove('active'));
587+
// Find button ... logic is duplicated? No, just hook it.
588+
// Actually simpler: just call loadConfig() once at startup too.
589+
}
590+
// Better: just add it to DOMContentLoaded if we want it preloaded,
591+
// or in the HTML onclick="switchView('settings'); loadConfig()" ?
592+
// Let's just modify the switchView function in place if possible,
593+
// but since I'm appending, I can just overload/wrap it?
594+
// The previous switchView was defined as `function switchView(...)`.
595+
// We can overwrite it?
596+
// Yes.
597+
const _oldSwitch = switchView;
598+
switchView = function (name) {
599+
_oldSwitch(name);
600+
if (name === 'settings') loadConfig();
601+
}

config.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"CHIRPSTACK_API_URL": "https://chirpstack.example.com",
3+
"CHIRPSTACK_API_TOKEN": "your_token_here",
4+
"GITHUB_REPO": "Princeddn/chirp-api",
5+
"GITHUB_BRANCH": "data-backup"
6+
}

index.html

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,10 @@
142142
<i class="bi bi-terminal-fill"></i>
143143
<span>Console Live</span>
144144
</button>
145+
<button class="nav-item" onclick="switchView('settings')">
146+
<i class="bi bi-sliders"></i>
147+
<span>Paramètres</span>
148+
</button>
145149

146150
<div class="menu-header">Gestion</div>
147151
<button class="nav-item" onclick="switchView('devices')">
@@ -383,6 +387,51 @@ <h3><i class="bi bi-send-fill"></i> Downlink</h3>
383387
</div>
384388
</div>
385389

390+
<!-- 6. SETTINGS -->
391+
<div id="view-settings" class="view-section" style="display:none;">
392+
<div class="card">
393+
<div class="card-header">
394+
<h3>Configuration de la Plateforme</h3>
395+
</div>
396+
<div class="card-body">
397+
<form id="config-form" onsubmit="event.preventDefault(); saveConfig();">
398+
<div class="mb-3">
399+
<label class="form-label">URL API ChirpStack</label>
400+
<input type="text" id="conf-chirpstack-url" class="input-dark w-100"
401+
placeholder="https://chirpstack.example.com">
402+
<small class="text-muted">L'adresse de votre instance ChirpStack.</small>
403+
</div>
404+
<div class="mb-3">
405+
<label class="form-label">Token API ChirpStack</label>
406+
<input type="password" id="conf-chirpstack-token" class="input-dark w-100" placeholder="API Token">
407+
<small class="text-muted">Clé d'API avec droits de lecture/écriture.</small>
408+
<div class="form-check mt-1">
409+
<input type="checkbox" class="form-check-input" onclick="togglePassword('conf-chirpstack-token')">
410+
<label class="form-check-label small">Afficher</label>
411+
</div>
412+
</div>
413+
414+
<hr style="border-color:var(--border-color); margin: 2rem 0;">
415+
416+
<div class="mb-3">
417+
<label class="form-label">Repo GitHub (Sauvegarde)</label>
418+
<input type="text" id="conf-github-repo" class="input-dark w-100" placeholder="User/Repo">
419+
</div>
420+
<div class="mb-3">
421+
<label class="form-label">Branche GitHub</label>
422+
<input type="text" id="conf-github-branch" class="input-dark w-100" placeholder="main">
423+
</div>
424+
425+
<div class="d-flex justify-content-end mt-4">
426+
<button type="submit" class="btn-primary-glow">
427+
<i class="bi bi-save"></i> Enregistrer les modifications
428+
</button>
429+
</div>
430+
</form>
431+
</div>
432+
</div>
433+
</div>
434+
386435
</main>
387436
</div>
388437

0 commit comments

Comments
 (0)