Skip to content

Commit 304a6b8

Browse files
Fotios Tsakiridisclaude
andcommitted
Release v2.83.4 - Problem Tickets Badge & Filters
Added: - Problem tickets badge (warning icon) in header - Stuck and Problems filter buttons in tickets list Fixed: - Project preview URL now includes secure key Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 50dddad commit 304a6b8

13 files changed

Lines changed: 156 additions & 23 deletions

CHANGELOG.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,25 @@ All notable changes to CodeHero will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [2.83.4] - 2026-01-26
9+
10+
### Added
11+
- **Problem Tickets Badge** - Warning icon in header for stuck/failed tickets
12+
- Pulsing red badge appears when tickets need attention
13+
- Click to navigate to filtered problem tickets view
14+
- Auto-refreshes every 30 seconds
15+
- Non-intrusive injection via Flask after_request
16+
17+
- **Tickets Filter Enhancement** - New filter buttons
18+
- Added "Stuck" filter for stuck tickets only
19+
- Added "Problems" filter for both stuck and failed tickets
20+
- Red-highlighted Problems button for visibility
21+
22+
### Fixed
23+
- **Project Preview URL** - Now includes secure key
24+
- "Open" link in project settings includes `?key=` parameter
25+
- Prevents access denied errors when opening preview
26+
827
## [2.83.3] - 2026-01-26
928

1029
### Improved

INSTALL.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
```bash
1414
cd /root
15-
unzip codehero-2.83.3.zip
15+
unzip codehero-2.83.4.zip
1616
cd codehero
1717
```
1818

@@ -129,8 +129,8 @@ sudo systemctl restart codehero-web codehero-daemon
129129
```bash
130130
# Download and extract new version
131131
cd /root
132-
wget https://github.com/fotsakir/codehero/releases/latest/download/codehero-2.83.3.zip
133-
unzip codehero-2.83.3.zip
132+
wget https://github.com/fotsakir/codehero/releases/latest/download/codehero-2.83.4.zip
133+
unzip codehero-2.83.4.zip
134134
cd codehero
135135

136136
# Preview what will change (recommended)

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
<p align="center">
1313
<a href="LICENSE"><img src="https://img.shields.io/badge/License-Dual-blue.svg" alt="License"></a>
14-
<a href="CHANGELOG.md"><img src="https://img.shields.io/badge/version-2.83.3-green.svg" alt="Version"></a>
14+
<a href="CHANGELOG.md"><img src="https://img.shields.io/badge/version-2.83.4-green.svg" alt="Version"></a>
1515
<img src="https://img.shields.io/badge/Ubuntu-22.04%20|%2024.04-orange.svg" alt="Ubuntu">
1616
<a href="https://anthropic.com"><img src="https://img.shields.io/badge/Powered%20by-Claude%20AI-blueviolet.svg" alt="Claude AI"></a>
1717
<a href="https://github.com/fotsakir/codehero/stargazers"><img src="https://img.shields.io/github/stars/fotsakir/codehero?style=social" alt="Stars"></a>
@@ -378,8 +378,8 @@ apt-get update && apt-get install -y unzip wget net-tools
378378

379379
# Download and extract
380380
cd /root
381-
wget https://github.com/fotsakir/codehero/releases/latest/download/codehero-2.83.3.zip
382-
unzip codehero-2.83.3.zip
381+
wget https://github.com/fotsakir/codehero/releases/latest/download/codehero-2.83.4.zip
382+
unzip codehero-2.83.4.zip
383383
cd codehero
384384

385385
# Run setup
@@ -405,7 +405,7 @@ The installer automatically sets up:
405405
```bash
406406
# Download new version
407407
cd /root
408-
unzip codehero-2.83.3.zip
408+
unzip codehero-2.83.4.zip
409409
cd codehero
410410

411411
# Preview changes (recommended)

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2.83.3
1+
2.83.4

docs/USER_GUIDE.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -557,8 +557,8 @@ Fix:
557557
558558
```bash
559559
cd /root
560-
wget https://github.com/fotsakir/codehero/releases/latest/download/codehero-2.83.3.zip
561-
unzip codehero-2.83.3.zip
560+
wget https://github.com/fotsakir/codehero/releases/latest/download/codehero-2.83.4.zip
561+
unzip codehero-2.83.4.zip
562562
cd codehero
563563
sudo ./upgrade.sh
564564
```

docs/VM_INSTALLATION.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -345,10 +345,10 @@ sudo su
345345
cd /root
346346

347347
# Download the latest release
348-
wget https://github.com/fotsakir/codehero/releases/latest/download/codehero-2.83.3.zip
348+
wget https://github.com/fotsakir/codehero/releases/latest/download/codehero-2.83.4.zip
349349

350350
# Extract
351-
unzip codehero-2.83.3.zip
351+
unzip codehero-2.83.4.zip
352352

353353
# Enter the folder
354354
cd codehero

docs/WSL_INSTALL.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,10 +131,10 @@ apt-get install -y unzip wget curl
131131

132132
# Download latest release
133133
cd /root
134-
wget https://github.com/fotsakir/codehero/releases/latest/download/codehero-2.83.3.zip
134+
wget https://github.com/fotsakir/codehero/releases/latest/download/codehero-2.83.4.zip
135135

136136
# Extract and install
137-
unzip codehero-2.83.3.zip
137+
unzip codehero-2.83.4.zip
138138
cd codehero
139139
chmod +x setup.sh
140140
./setup.sh

docs/index.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
"description": "The Developer That Never Rests. Self-hosted autonomous AI coding agent. Give it tasks. Walk away. Wake up to working code.",
4242
"url": "https://fotsakir.github.io/codehero/",
4343
"downloadUrl": "https://github.com/fotsakir/codehero/releases/latest",
44-
"softwareVersion": "2.83.3",
44+
"softwareVersion": "2.83.4",
4545
"applicationCategory": "DeveloperApplication",
4646
"operatingSystem": "Ubuntu 22.04, Ubuntu 24.04",
4747
"offers": {
@@ -1330,9 +1330,9 @@ <h1>CodeHero</h1>
13301330
Install on <strong>Ubuntu 22.04/24.04</strong> VM (VirtualBox, VMware, Hyper-V, or cloud VPS).
13311331
</p>
13321332
<div style="position: relative; background: rgba(0,0,0,0.3); padding: 0.8rem 3rem 0.8rem 0.8rem; border-radius: 6px; font-family: 'JetBrains Mono', monospace; font-size: 0.7rem; overflow-x: auto;">
1333-
<button onclick="copyCode(this, 'wget https://github.com/fotsakir/codehero/releases/latest/download/codehero-2.83.3.zip\nunzip codehero-*.zip && cd codehero && ./setup.sh')" style="position: absolute; top: 6px; right: 6px; background: rgba(255,255,255,0.1); border: none; color: var(--text-muted); padding: 4px 8px; border-radius: 4px; cursor: pointer; font-size: 0.7rem; transition: all 0.2s;" onmouseover="this.style.background='rgba(255,255,255,0.2)'" onmouseout="this.style.background='rgba(255,255,255,0.1)'">📋</button>
1333+
<button onclick="copyCode(this, 'wget https://github.com/fotsakir/codehero/releases/latest/download/codehero-2.83.4.zip\nunzip codehero-*.zip && cd codehero && ./setup.sh')" style="position: absolute; top: 6px; right: 6px; background: rgba(255,255,255,0.1); border: none; color: var(--text-muted); padding: 4px 8px; border-radius: 4px; cursor: pointer; font-size: 0.7rem; transition: all 0.2s;" onmouseover="this.style.background='rgba(255,255,255,0.2)'" onmouseout="this.style.background='rgba(255,255,255,0.1)'">📋</button>
13341334
<span style="color: var(--text-muted);"># On Ubuntu VM (as root)</span><br>
1335-
<span style="color: var(--accent-cyan);">wget https://github.com/fotsakir/codehero/releases/latest/download/codehero-2.83.3.zip</span><br>
1335+
<span style="color: var(--accent-cyan);">wget https://github.com/fotsakir/codehero/releases/latest/download/codehero-2.83.4.zip</span><br>
13361336
<span style="color: var(--accent-cyan);">unzip codehero-*.zip && cd codehero && ./setup.sh</span>
13371337
</div>
13381338
<p style="margin-top: 0.8rem; font-size: 0.8rem;">

scripts/setup_domain.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,8 @@ get_letsencrypt_cert() {
219219
# Stop nginx temporarily for standalone mode
220220
systemctl stop nginx
221221

222-
if certbot certonly --standalone -d "$domain" --email "$email" --agree-tos --non-interactive; then
222+
# Use RSA key for broader compatibility (ECDSA not supported by older devices)
223+
if certbot certonly --standalone -d "$domain" --email "$email" --agree-tos --non-interactive --key-type rsa; then
223224
systemctl start nginx
224225
log_success "Certificate obtained for $domain"
225226
return 0

web/app.py

Lines changed: 106 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,86 @@ def safe_filename(filename):
113113
socketio = SocketIO(app, cors_allowed_origins="*", async_mode='eventlet', allow_upgrades=True)
114114

115115
@app.context_processor
116-
def inject_version():
117-
return {'version': VERSION}
116+
def inject_globals():
117+
"""Inject global variables available in all templates."""
118+
result = {'version': VERSION, 'problem_tickets': 0}
119+
120+
# Get problem tickets count (stuck + failed)
121+
try:
122+
conn = get_db()
123+
cursor = conn.cursor(dictionary=True)
124+
cursor.execute("SELECT COUNT(*) as cnt FROM tickets WHERE status IN ('stuck', 'failed')")
125+
result['problem_tickets'] = cursor.fetchone()['cnt']
126+
cursor.close()
127+
conn.close()
128+
except:
129+
pass
130+
131+
return result
132+
133+
134+
@app.after_request
135+
def inject_problem_badge(response):
136+
"""Inject problem tickets badge script into all HTML responses."""
137+
# Only for HTML responses
138+
if not response.content_type or not response.content_type.startswith('text/html'):
139+
return response
140+
141+
# Check if user is logged in
142+
if 'user' not in session:
143+
return response
144+
145+
try:
146+
data = response.get_data(as_text=True)
147+
if '</body>' not in data:
148+
return response
149+
150+
script = '''<script>
151+
(function(){
152+
function updateProblemBadge(){
153+
fetch('/api/problem-tickets-count')
154+
.then(r=>r.json())
155+
.then(d=>{
156+
let b=document.getElementById('problemBadge');
157+
if(d.count>0){
158+
if(!b){
159+
b=document.createElement('a');
160+
b.id='problemBadge';
161+
b.href='/tickets?status=problems';
162+
b.style.cssText='display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;font-size:14px;line-height:1;padding-bottom:2px;background:linear-gradient(135deg,#ef4444,#dc2626);border-radius:6px;text-decoration:none;animation:pulse-problem 2s infinite;cursor:pointer';
163+
b.title='Problem tickets (stuck/failed)';
164+
b.textContent='\\u26A0\\uFE0F';
165+
let header=document.querySelector('.header-left');
166+
if(header) header.appendChild(b);
167+
else{
168+
let h1=document.querySelector('.header h1');
169+
if(h1&&h1.parentElement) h1.parentElement.appendChild(b);
170+
}
171+
}
172+
b.style.display='inline-flex';
173+
} else if(b) b.style.display='none';
174+
}).catch(()=>{});
175+
}
176+
if(!document.getElementById('problemBadgeStyle')){
177+
let s=document.createElement('style');
178+
s.id='problemBadgeStyle';
179+
s.textContent='@keyframes pulse-problem{0%,100%{opacity:1;box-shadow:0 0 0 0 rgba(239,68,68,0.4)}50%{opacity:0.9;box-shadow:0 0 10px 2px rgba(239,68,68,0.3)}}';
180+
document.head.appendChild(s);
181+
}
182+
if(document.readyState==='loading'){
183+
document.addEventListener('DOMContentLoaded',updateProblemBadge);
184+
}else{
185+
updateProblemBadge();
186+
}
187+
setInterval(updateProblemBadge,30000);
188+
})();
189+
</script>'''
190+
data = data.replace('</body>', script + '</body>')
191+
response.set_data(data)
192+
except Exception as e:
193+
pass # Don't break page if injection fails
194+
195+
return response
118196

119197

120198
# Configure logging for error tracking
@@ -784,7 +862,7 @@ def validate_project_key():
784862
max_age=86400*7,
785863
path='/' + project_folder,
786864
httponly=True,
787-
secure=True,
865+
secure=request.is_secure, # True only for HTTPS, False for HTTP
788866
samesite='Lax'
789867
)
790868
return response
@@ -837,6 +915,10 @@ def dashboard():
837915
cursor.execute("SELECT COUNT(*) as cnt FROM tickets WHERE status = 'done' AND DATE(updated_at) = CURDATE()")
838916
stats['completed_today'] = cursor.fetchone()['cnt']
839917

918+
# Problem tickets (stuck + failed) for notification
919+
cursor.execute("SELECT COUNT(*) as cnt FROM tickets WHERE status IN ('stuck', 'failed')")
920+
stats['problem_tickets'] = cursor.fetchone()['cnt']
921+
840922
# Daemon status
841923
if os.path.exists(PID_FILE):
842924
try:
@@ -900,6 +982,8 @@ def tickets_list():
900982
if status_filter:
901983
if status_filter == 'open':
902984
query += " AND t.status IN ('new', 'open', 'pending')"
985+
elif status_filter == 'problems':
986+
query += " AND t.status IN ('stuck', 'failed')"
903987
else:
904988
query += " AND t.status = %s"
905989
params.append(status_filter)
@@ -926,6 +1010,9 @@ def tickets_list():
9261010
'in_progress': 'In Progress',
9271011
'awaiting_input': 'Awaiting Input',
9281012
'done': 'Completed' + (' Today' if today_only == '1' else ''),
1013+
'stuck': 'Stuck Tickets',
1014+
'failed': 'Failed Tickets',
1015+
'problems': 'Problem Tickets',
9291016
'': 'All Tickets'
9301017
}
9311018
title = title_map.get(status_filter, f'{status_filter.title()} Tickets')
@@ -1045,6 +1132,22 @@ def project_progress_view(project_id):
10451132
project=project)
10461133

10471134

1135+
@app.route('/api/problem-tickets-count')
1136+
@login_required
1137+
def api_problem_tickets_count():
1138+
"""Get count of stuck + failed tickets for header notification badge."""
1139+
try:
1140+
conn = get_db()
1141+
cursor = conn.cursor(dictionary=True)
1142+
cursor.execute("SELECT COUNT(*) as cnt FROM tickets WHERE status IN ('stuck', 'failed')")
1143+
count = cursor.fetchone()['cnt']
1144+
cursor.close()
1145+
conn.close()
1146+
return jsonify({'count': count})
1147+
except:
1148+
return jsonify({'count': 0})
1149+
1150+
10481151
@app.route('/api/project/<int:project_id>/archive', methods=['POST'])
10491152
@login_required
10501153
def archive_project(project_id):

0 commit comments

Comments
 (0)