Skip to content

Commit 654e081

Browse files
fix(config): Add environment-aware API configuration system (#24)
Replace hardcoded API_BASE URLs with environment-aware configuration: - dashboard.html: Replace 'http://localhost:8000' with environment detection - governance_dashboard.html: Replace '' with environment detection Environment-aware configuration: - Detects dev environment via hostname or window.LEXECON_ENV - Uses localhost:8000 in development - Uses window.location.origin + '/api' in production - Allows manual override via window.LEXECON_API_BASE Fixes #18 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 568ac0d commit 654e081

2 files changed

Lines changed: 59 additions & 42 deletions

File tree

dashboard.html

Lines changed: 50 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@
374374
0% { transform: rotate(0deg); }
375375
100% { transform: rotate(360deg); }
376376
}
377-
377+
378378
.modal-overlay {
379379
display: none;
380380
position: fixed;
@@ -1046,7 +1046,16 @@ <h2 class="section-title">
10461046
</div>
10471047

10481048
<script>
1049-
const API_BASE = 'http://localhost:8000';
1049+
// Environment configuration
1050+
// In production, window.LEXECON_ENV will be undefined or 'production'
1051+
// To enable dev mode, set: <script>window.LEXECON_ENV = 'development';</script> before this file
1052+
const ENV = window.LEXECON_ENV || 'production';
1053+
const IS_DEV = ENV === 'development' || window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1';
1054+
1055+
// API Base URL - environment aware
1056+
const API_BASE = window.LEXECON_API_BASE ||
1057+
(IS_DEV ? 'http://localhost:8000' : window.location.origin + '/api');
1058+
10501059
let currentTimeWindow = 'all';
10511060

10521061
async function fetchAPI(endpoint) {
@@ -1256,7 +1265,7 @@ <h2 class="section-title">
12561265
document.getElementById('auditModal').style.display = 'block';
12571266
currentStep = 1;
12581267
updateModalUI();
1259-
1268+
12601269
// Pre-fill current user if available
12611270
const today = new Date().toISOString().split('T')[0];
12621271
document.getElementById('dateTo').value = today;
@@ -1344,7 +1353,7 @@ <h2 class="section-title">
13441353
const requester = document.getElementById('requesterName').value.trim();
13451354
const email = document.getElementById('requesterEmail').value.trim();
13461355
const purpose = document.getElementById('requestPurpose').value;
1347-
1356+
13481357
if (!requester) {
13491358
alert('❌ Please enter your name');
13501359
return false;
@@ -1361,10 +1370,10 @@ <h2 class="section-title">
13611370
}
13621371

13631372
function validateStep2() {
1364-
const hasFormat = document.getElementById('formatJSON').checked ||
1365-
document.getElementById('formatText').checked ||
1373+
const hasFormat = document.getElementById('formatJSON').checked ||
1374+
document.getElementById('formatText').checked ||
13661375
document.getElementById('formatCSV').checked;
1367-
1376+
13681377
if (!hasFormat) {
13691378
alert('❌ Please select at least one export format');
13701379
return false;
@@ -1386,7 +1395,7 @@ <h2 class="section-title">
13861395
function captureStep2Data() {
13871396
const dateFrom = document.getElementById('dateFrom').value;
13881397
const dateTo = document.getElementById('dateTo').value;
1389-
1398+
13901399
auditPacketData.config = {
13911400
date_range: {
13921401
from: dateFrom || null,
@@ -1409,35 +1418,35 @@ <h2 class="section-title">
14091418
async function updatePreview() {
14101419
// Update metadata preview
14111420
document.getElementById('previewRequester').textContent = auditPacketData.metadata.requester_name;
1412-
document.getElementById('previewPurpose').textContent =
1421+
document.getElementById('previewPurpose').textContent =
14131422
document.getElementById('requestPurpose').options[document.getElementById('requestPurpose').selectedIndex].text;
14141423
document.getElementById('previewCaseId').textContent = auditPacketData.metadata.case_id;
1415-
1424+
14161425
// Update date range
14171426
const from = auditPacketData.config.date_range.from || 'All time';
14181427
const to = auditPacketData.config.date_range.to;
1419-
document.getElementById('previewDateRange').textContent =
1428+
document.getElementById('previewDateRange').textContent =
14201429
from === 'All time' ? 'All time' : `${from} to ${to}`;
1421-
1430+
14221431
// Update formats
14231432
const formats = [];
14241433
if (auditPacketData.config.formats.json) formats.push('JSON');
14251434
if (auditPacketData.config.formats.text) formats.push('Text');
14261435
if (auditPacketData.config.formats.csv) formats.push('CSV');
14271436
document.getElementById('previewFormats').textContent = formats.join(', ');
1428-
1437+
14291438
// Fetch actual counts from API (simplified - you'd call real endpoints)
14301439
try {
14311440
const timeWindow = calculateTimeWindow();
14321441
const [ledger, storage] = await Promise.all([
14331442
fetchAPI('/ledger/entries'),
14341443
fetchAPI('/compliance/eu-ai-act/article-14/storage/stats')
14351444
]);
1436-
1445+
14371446
const decisions = ledger?.entries?.filter(e => e.event_type === 'decision').length || 0;
14381447
const interventions = storage?.total_interventions || 0;
14391448
const ledgerEntries = ledger?.entries?.length || 0;
1440-
1449+
14411450
document.getElementById('previewDecisions').textContent = auditPacketData.config.include.decisions ? decisions : 'Excluded';
14421451
document.getElementById('previewInterventions').textContent = auditPacketData.config.include.interventions ? interventions : 'Excluded';
14431452
document.getElementById('previewLedger').textContent = auditPacketData.config.include.ledger ? ledgerEntries : 'Excluded';
@@ -1449,13 +1458,13 @@ <h2 class="section-title">
14491458
function calculateTimeWindow() {
14501459
const from = auditPacketData.config.date_range.from;
14511460
const to = auditPacketData.config.date_range.to;
1452-
1461+
14531462
if (!from) return 'all';
1454-
1463+
14551464
const fromDate = new Date(from);
14561465
const toDate = new Date(to);
14571466
const diffDays = Math.ceil((toDate - fromDate) / (1000 * 60 * 60 * 24));
1458-
1467+
14591468
if (diffDays <= 1) return '24h';
14601469
if (diffDays <= 7) return '7d';
14611470
if (diffDays <= 30) return '30d';
@@ -1466,7 +1475,7 @@ <h2 class="section-title">
14661475
const today = new Date();
14671476
const to = today.toISOString().split('T')[0];
14681477
document.getElementById('dateTo').value = to;
1469-
1478+
14701479
if (range === 'all') {
14711480
document.getElementById('dateFrom').value = '';
14721481
} else {
@@ -1556,45 +1565,45 @@ <h2 class="section-title">
15561565
document.getElementById('modalStep5').style.display = 'block';
15571566
document.getElementById('modalBackBtn').style.display = 'none';
15581567
document.getElementById('modalNextBtn').style.display = 'none';
1559-
1568+
15601569
const progressFill = document.getElementById('progressFill');
15611570
const statusText = document.getElementById('generationStatus');
1562-
1571+
15631572
try {
15641573
// Step 1: Validate request
15651574
progressFill.style.width = '20%';
15661575
statusText.textContent = 'Validating request...';
15671576
await sleep(300);
1568-
1577+
15691578
// Step 2: Collect data
15701579
progressFill.style.width = '40%';
15711580
statusText.textContent = 'Collecting compliance data...';
15721581
await sleep(300);
1573-
1582+
15741583
const timeWindow = calculateTimeWindow();
15751584
const formats = [];
15761585
if (auditPacketData.config.formats.json) formats.push('json');
15771586
if (auditPacketData.config.formats.text) formats.push('text');
15781587
if (auditPacketData.config.formats.csv) formats.push('csv');
1579-
1588+
15801589
// Step 3: Generate packets
15811590
progressFill.style.width = '60%';
15821591
statusText.textContent = 'Generating audit packets...';
1583-
1592+
15841593
const downloads = [];
15851594
for (const format of formats) {
15861595
const response = await fetch(`${API_BASE}/compliance/eu-ai-act/audit-packet?time_window=${timeWindow}&format=${format}`);
15871596
if (!response.ok) throw new Error(`Failed to generate ${format} format`);
1588-
1597+
15891598
const data = format === 'json' ? await response.json() : await response.text();
15901599
downloads.push({ format, data });
15911600
}
1592-
1601+
15931602
// Step 4: Add metadata
15941603
progressFill.style.width = '80%';
15951604
statusText.textContent = 'Adding request metadata...';
15961605
await sleep(200);
1597-
1606+
15981607
// Enhance JSON packet with metadata
15991608
if (auditPacketData.config.formats.json) {
16001609
const jsonDownload = downloads.find(d => d.format === 'json');
@@ -1603,21 +1612,21 @@ <h2 class="section-title">
16031612
jsonDownload.data.generation_config = auditPacketData.config;
16041613
}
16051614
}
1606-
1615+
16071616
// Step 5: Download files
16081617
progressFill.style.width = '100%';
16091618
statusText.textContent = 'Preparing downloads...';
16101619
await sleep(200);
1611-
1620+
16121621
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').substring(0, 19);
1613-
1622+
16141623
for (const download of downloads) {
16151624
const extension = download.format === 'json' ? 'json' : download.format === 'text' ? 'txt' : 'csv';
16161625
const content = download.format === 'json' ? JSON.stringify(download.data, null, 2) : download.data;
1617-
const blob = new Blob([content], {
1618-
type: download.format === 'json' ? 'application/json' : 'text/plain'
1626+
const blob = new Blob([content], {
1627+
type: download.format === 'json' ? 'application/json' : 'text/plain'
16191628
});
1620-
1629+
16211630
const url = window.URL.createObjectURL(blob);
16221631
const a = document.createElement('a');
16231632
a.href = url;
@@ -1626,13 +1635,13 @@ <h2 class="section-title">
16261635
a.click();
16271636
document.body.removeChild(a);
16281637
window.URL.revokeObjectURL(url);
1629-
1638+
16301639
await sleep(100); // Small delay between downloads
16311640
}
1632-
1641+
16331642
// Success!
16341643
closeAuditModal();
1635-
1644+
16361645
alert(`✅ Audit Packet Generated Successfully!
16371646

16381647
Requester: ${auditPacketData.metadata.requester_name}
@@ -1641,13 +1650,13 @@ <h2 class="section-title">
16411650
Files Downloaded: ${downloads.length}
16421651

16431652
This generation has been logged in the audit trail.`);
1644-
1653+
16451654
} catch (error) {
16461655
console.error('Generation failed:', error);
16471656
progressFill.style.width = '0%';
16481657
statusText.textContent = 'Generation failed';
16491658
statusText.style.color = 'var(--danger)';
1650-
1659+
16511660
setTimeout(() => {
16521661
alert('❌ Failed to generate audit packet:\n\n' + error.message);
16531662
closeAuditModal();
@@ -1686,7 +1695,7 @@ <h2 class="section-title">
16861695
<h2 class="modal-title">Generate Compliance Audit Packet</h2>
16871696
<button class="modal-close" onclick="closeAuditModal()">×</button>
16881697
</div>
1689-
1698+
16901699
<div class="modal-body">
16911700
<!-- Step Indicator -->
16921701
<div class="step-indicator">
@@ -2016,7 +2025,7 @@ <h3 style="color: var(--text-primary); margin-bottom: 8px;">Generating Audit Pac
20162025
</div>
20172026
</div>
20182027
</div>
2019-
2028+
20202029
<div class="modal-footer">
20212030
<button class="action-btn btn-secondary" id="modalBackBtn" onclick="previousStep()" style="display: none;">
20222031
← Back

governance_dashboard.html

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -891,7 +891,15 @@ <h3 class="modal-title">Assess Risk for Decision</h3>
891891
</div>
892892

893893
<script>
894-
const API_BASE = ''; // Same origin
894+
// Environment configuration
895+
// In production, window.LEXECON_ENV will be undefined or 'production'
896+
// To enable dev mode, set: <script>window.LEXECON_ENV = 'development';</script> before this file
897+
const ENV = window.LEXECON_ENV || 'production';
898+
const IS_DEV = ENV === 'development' || window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1';
899+
900+
// API Base URL - environment aware
901+
const API_BASE = window.LEXECON_API_BASE ||
902+
(IS_DEV ? 'http://localhost:8000' : window.location.origin + '/api');
895903

896904
// Initialize dashboard
897905
document.addEventListener('DOMContentLoaded', () => {

0 commit comments

Comments
 (0)