Skip to content

Commit 3a49e6f

Browse files
authored
feat(localgov-drupal): Add live log viewer to init status page (#27)
- Add terminal-style log viewer showing last 50 lines of init output - Capture init logs to file using tee while preserving Docker logs - Add nginx location for /init-log.txt with no-cache headers - Change auto-refresh from 5s to 3s for more responsive updates - Update CloudFormation DrupalUrl output to point to /init-status - Auto-redirect to home page when initialization completes
1 parent be9b3a7 commit 3a49e6f

4 files changed

Lines changed: 77 additions & 8 deletions

File tree

cloudformation/scenarios/localgov-drupal/cdk/lib/localgov-drupal-stack.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,10 @@ export class LocalGovDrupalStack extends cdk.Stack {
104104
// ==========================================================================
105105

106106
// Drupal URL - primary access point (HTTPS via CloudFront)
107+
// Points to init-status page which redirects to home when initialization is complete
107108
new cdk.CfnOutput(this, 'DrupalUrl', {
108109
description: 'URL to access LocalGov Drupal (HTTPS)',
109-
value: `https://${cdn.domainName}`,
110+
value: `https://${cdn.domainName}/init-status`,
110111
exportName: `${this.stackName}-DrupalUrl`,
111112
});
112113

cloudformation/scenarios/localgov-drupal/docker/config/nginx.conf

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,14 @@ http {
8787
try_files /init-status.json =503;
8888
}
8989

90+
# Init log file (plain text, no caching for live updates)
91+
location = /init-log.txt {
92+
default_type text/plain;
93+
add_header Cache-Control "no-cache, no-store, must-revalidate";
94+
add_header Pragma "no-cache";
95+
try_files /init-log.txt =503;
96+
}
97+
9098
# Block access to sensitive files
9199
location ~ /\. {
92100
deny all;

cloudformation/scenarios/localgov-drupal/docker/entrypoint.sh

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,11 @@ if [ "${SKIP_INIT:-false}" != "true" ]; then
4949
# Run initialization script if it exists
5050
if [ -f /scripts/init-drupal.sh ]; then
5151
echo "Running Drupal initialization..."
52-
/scripts/init-drupal.sh
52+
# Capture logs to file for web-based status viewer while preserving stdout for Docker logs
53+
INIT_LOG_FILE="/var/www/drupal/web/init-log.txt"
54+
touch "$INIT_LOG_FILE"
55+
chmod 644 "$INIT_LOG_FILE"
56+
/scripts/init-drupal.sh 2>&1 | tee "$INIT_LOG_FILE"
5357
fi
5458
fi
5559

cloudformation/scenarios/localgov-drupal/docker/scripts/init-drupal.sh

Lines changed: 62 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,20 @@ log() {
2626
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
2727
}
2828

29+
# Log file location (written by entrypoint.sh via tee)
30+
INIT_LOG_FILE="/var/www/drupal/web/init-log.txt"
31+
32+
# Get the last N lines of the log file, HTML-escaped for embedding in status page
33+
get_log_tail() {
34+
local lines="${1:-50}"
35+
if [ -f "$INIT_LOG_FILE" ] && [ -s "$INIT_LOG_FILE" ]; then
36+
tail -n "$lines" "$INIT_LOG_FILE" 2>/dev/null | \
37+
sed 's/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g; s/"/\&quot;/g'
38+
else
39+
echo "Waiting for initialization to start..."
40+
fi
41+
}
42+
2943
# Helper function to run drush commands with retry logic for deadlocks
3044
# Handles Aurora MySQL deadlocks (SQLSTATE 40001) that occur during cache writes
3145
run_drush_with_retry() {
@@ -65,19 +79,23 @@ update_status() {
6579

6680
log "Status: $phase - $message"
6781

68-
# Create HTML status page
82+
# Get log content for embedding in status page
83+
local LOG_CONTENT
84+
LOG_CONTENT=$(get_log_tail 50)
85+
86+
# Create HTML status page with embedded log viewer
6987
cat > "$STATUS_FILE" << EOF
7088
<!DOCTYPE html>
7189
<html lang="en">
7290
<head>
7391
<meta charset="UTF-8">
7492
<meta name="viewport" content="width=device-width, initial-scale=1.0">
75-
<meta http-equiv="refresh" content="5">
93+
<meta http-equiv="refresh" content="3">
7694
<title>LocalGov Drupal - Initialization</title>
7795
<style>
7896
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
79-
max-width: 600px; margin: 50px auto; padding: 20px; background: #f3f2f1; }
80-
.card { background: white; border-radius: 8px; padding: 30px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
97+
max-width: 900px; margin: 30px auto; padding: 20px; background: #f3f2f1; }
98+
.card { background: white; border-radius: 8px; padding: 30px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); margin-bottom: 20px; }
8199
h1 { color: #0b0c0c; margin-bottom: 10px; }
82100
.phase { font-size: 1.2em; color: #1d70b8; margin-bottom: 20px; }
83101
.message { color: #505a5f; margin-bottom: 20px; }
@@ -89,7 +107,37 @@ update_status() {
89107
@keyframes spin { to { transform: rotate(360deg); } }
90108
.error { color: #d4351c; }
91109
.success { color: #00703c; }
110+
111+
/* Log viewer styles */
112+
.log-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px; }
113+
.log-title { font-size: 1em; font-weight: bold; color: #0b0c0c; margin: 0; }
114+
.log-info { font-size: 0.8em; color: #505a5f; }
115+
.log-viewer {
116+
background: #1d1d1d;
117+
color: #00ff00;
118+
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
119+
font-size: 12px;
120+
line-height: 1.4;
121+
padding: 15px;
122+
border-radius: 6px;
123+
height: 300px;
124+
overflow-y: auto;
125+
white-space: pre-wrap;
126+
word-wrap: break-word;
127+
}
128+
.log-viewer::-webkit-scrollbar { width: 8px; }
129+
.log-viewer::-webkit-scrollbar-track { background: #2d2d2d; }
130+
.log-viewer::-webkit-scrollbar-thumb { background: #555; border-radius: 4px; }
92131
</style>
132+
<script>
133+
// Auto-scroll log to bottom on load
134+
window.onload = function() {
135+
var logViewer = document.getElementById('log-viewer');
136+
if (logViewer) {
137+
logViewer.scrollTop = logViewer.scrollHeight;
138+
}
139+
};
140+
</script>
93141
</head>
94142
<body>
95143
<div class="card">
@@ -101,14 +149,22 @@ update_status() {
101149
</div>
102150
<div class="status">
103151
$(if [ "$status" = "error" ]; then
104-
echo '<span class="error">Error occurred</span>'
152+
echo '<span class="error">Error occurred</span>'
105153
elif [ "$status" = "complete" ]; then
106-
echo '<span class="success">Complete - Redirecting...</span><script>setTimeout(function(){window.location.href="/"},3000);</script>'
154+
echo '<span class="success">Complete - Redirecting...</span><script>setTimeout(function(){window.location.href="/"},3000);</script>'
107155
else
108156
echo '<span class="spinner"></span> Please wait...'
109157
fi)
110158
</div>
111159
</div>
160+
161+
<div class="card">
162+
<div class="log-header">
163+
<h2 class="log-title">Initialization Log</h2>
164+
<span class="log-info">Last 50 lines - Auto-refreshes every 3 seconds</span>
165+
</div>
166+
<pre class="log-viewer" id="log-viewer">$LOG_CONTENT</pre>
167+
</div>
112168
</body>
113169
</html>
114170
EOF

0 commit comments

Comments
 (0)