diff --git a/package/secubox/secubox-app-streamlit-evolution/README.md b/package/secubox/secubox-app-streamlit-evolution/README.md new file mode 100644 index 00000000..9650de1c --- /dev/null +++ b/package/secubox/secubox-app-streamlit-evolution/README.md @@ -0,0 +1,30 @@ +# SecuBox Evolution Dashboard + +Interactive Streamlit dashboard showing SecuBox project evolution. + +## Features + +- **Real-time GitHub sync**: Fetches HISTORY.md, WIP.md, TODO.md, README.md from master branch +- **Milestone tracking**: Parses and displays project milestones with dates +- **Search**: Full-text search across all project files +- **Timeline view**: Visual timeline of project evolution +- **Feature distribution**: Charts showing feature category breakdown +- **Dark cyberpunk theme**: Matches SecuBox design language + +## Deployment + +1. Copy `secubox_evolution.py` to `/srv/streamlit/apps/` +2. Add instance: `uci set streamlit.secubox_evolution=instance && uci set streamlit.secubox_evolution.enabled='1' && uci set streamlit.secubox_evolution.app='secubox_evolution' && uci set streamlit.secubox_evolution.port='8510' && uci commit streamlit` +3. Restart: `/etc/init.d/streamlit restart` +4. Access: `http://:8510` + +## Dependencies + +- streamlit >= 1.32.0 +- pandas >= 2.0.0 +- requests >= 2.31.0 + +## Data Sources + +- GitHub: `https://raw.githubusercontent.com/gkerma/secubox-openwrt/master/.claude/` +- Auto-refresh: 5 minutes cache TTL diff --git a/package/secubox/secubox-app-streamlit-evolution/files/requirements.txt b/package/secubox/secubox-app-streamlit-evolution/files/requirements.txt new file mode 100644 index 00000000..7441f310 --- /dev/null +++ b/package/secubox/secubox-app-streamlit-evolution/files/requirements.txt @@ -0,0 +1,3 @@ +streamlit>=1.32.0 +pandas>=2.0.0 +requests>=2.31.0 diff --git a/package/secubox/secubox-app-streamlit-evolution/files/secubox_evolution.py b/package/secubox/secubox-app-streamlit-evolution/files/secubox_evolution.py new file mode 100644 index 00000000..2f28a0f6 --- /dev/null +++ b/package/secubox/secubox-app-streamlit-evolution/files/secubox_evolution.py @@ -0,0 +1,468 @@ +#!/usr/bin/env python3 +""" +SecuBox Evolution Dashboard +Interactive Streamlit landing page showing project evolution, history, WIP, TODO, and README +""" + +import streamlit as st +import requests +import re +from datetime import datetime +from collections import Counter + +# Page config +st.set_page_config( + page_title="SecuBox Evolution", + page_icon="🛡️", + layout="wide", + initial_sidebar_state="expanded" +) + +# Custom CSS for dark cyberpunk theme +st.markdown(""" + +""", unsafe_allow_html=True) + +# GitHub raw URLs +GITHUB_BASE = "https://raw.githubusercontent.com/gkerma/secubox-openwrt/master" +FILES = { + "HISTORY": f"{GITHUB_BASE}/.claude/HISTORY.md", + "WIP": f"{GITHUB_BASE}/.claude/WIP.md", + "TODO": f"{GITHUB_BASE}/.claude/TODO.md", + "README": f"{GITHUB_BASE}/README.md" +} + +@st.cache_data(ttl=300) +def fetch_file(url): + """Fetch file content from GitHub""" + try: + response = requests.get(url, timeout=10) + if response.status_code == 200: + return response.text + return None + except: + return None + +def parse_history(content): + """Parse HISTORY.md to extract milestones""" + if not content: + return [] + + milestones = [] + # Match patterns like "1. **Title (2026-02-06)**" or "14. **P2P MirrorBox..." + pattern = r'(\d+)\.\s+\*\*([^*]+)\*\*' + + for match in re.finditer(pattern, content): + num = match.group(1) + title = match.group(2).strip() + + # Extract date if present + date_match = re.search(r'\((\d{4}-\d{2}-\d{2})\)', title) + date = date_match.group(1) if date_match else None + title_clean = re.sub(r'\s*\(\d{4}-\d{2}-\d{2}\)\s*', '', title) + + milestones.append({ + 'num': int(num), + 'title': title_clean, + 'date': date + }) + + return milestones + +def parse_packages(content): + """Extract package names from content""" + if not content: + return [] + + packages = set() + # Match `package-name` or secubox-app-xxx patterns + patterns = [ + r'`(secubox-[a-z0-9-]+)`', + r'`(luci-app-[a-z0-9-]+)`', + r'\*\*([a-z0-9-]+)\*\*:', + ] + + for pattern in patterns: + for match in re.finditer(pattern, content): + pkg = match.group(1) + if len(pkg) > 3: + packages.add(pkg) + + return list(packages) + +def count_features(content): + """Count features mentioned in content""" + if not content: + return {} + + features = { + 'AI/LocalAI': len(re.findall(r'LocalAI|AI-powered|LLM|agent', content, re.I)), + 'Security': len(re.findall(r'CrowdSec|WAF|firewall|threat|CVE|security', content, re.I)), + 'DNS': len(re.findall(r'DNS|Vortex|dnsctl|AdGuard', content, re.I)), + 'Mesh/P2P': len(re.findall(r'mesh|P2P|gossip|mirror|peer', content, re.I)), + 'Containers': len(re.findall(r'LXC|Docker|container', content, re.I)), + 'UI/LuCI': len(re.findall(r'LuCI|dashboard|UI|interface', content, re.I)), + } + return features + +def main(): + # Header + st.markdown('

🛡️ SecuBox Evolution

', unsafe_allow_html=True) + st.markdown('

Real-time project tracking • History • WIP • TODO • Documentation

', unsafe_allow_html=True) + + # Fetch all files + with st.spinner("Fetching latest data from GitHub..."): + history = fetch_file(FILES["HISTORY"]) + wip = fetch_file(FILES["WIP"]) + todo = fetch_file(FILES["TODO"]) + readme = fetch_file(FILES["README"]) + + # Parse data + milestones = parse_history(history) + packages = parse_packages(history or "") + features = count_features(history or "") + + # Metrics row + col1, col2, col3, col4 = st.columns(4) + + with col1: + st.markdown(f""" +
+
{len(milestones)}
+
Milestones
+
+ """, unsafe_allow_html=True) + + with col2: + st.markdown(f""" +
+
{len(packages)}
+
Packages
+
+ """, unsafe_allow_html=True) + + with col3: + # Count TODO items + todo_count = len(re.findall(r'^- \[[ x]\]', todo or "", re.M)) + st.markdown(f""" +
+
{todo_count}
+
TODO Items
+
+ """, unsafe_allow_html=True) + + with col4: + # Latest date + latest_date = "N/A" + for m in reversed(milestones): + if m['date']: + latest_date = m['date'] + break + st.markdown(f""" +
+
{latest_date}
+
Last Update
+
+ """, unsafe_allow_html=True) + + st.markdown("
", unsafe_allow_html=True) + + # Sidebar + with st.sidebar: + st.markdown("### 🔍 Search") + search_query = st.text_input("Search in all files", placeholder="e.g., CrowdSec, HAProxy...") + + st.markdown("### 📊 Feature Distribution") + if features: + for feat, count in sorted(features.items(), key=lambda x: -x[1]): + if count > 0: + st.progress(min(count / 50, 1.0), text=f"{feat}: {count}") + + st.markdown("### 🏷️ Recent Packages") + for pkg in packages[-10:]: + st.markdown(f'{pkg}', unsafe_allow_html=True) + + st.markdown("---") + st.markdown("### ⚡ Quick Links") + st.markdown("[GitHub Repository](https://github.com/gkerma/secubox-openwrt)") + st.markdown("[SecuBox Portal](https://secubox.in)") + + if st.button("🔄 Refresh Data"): + st.cache_data.clear() + st.rerun() + + # Main tabs + tab1, tab2, tab3, tab4, tab5 = st.tabs(["📜 History", "🔧 WIP", "📋 TODO", "📖 README", "📈 Timeline"]) + + with tab1: + st.markdown("## 📜 Project History") + + if search_query and history: + # Highlight search results + highlighted = history.replace(search_query, f'**:green[{search_query}]**') + st.markdown(highlighted) + elif history: + # Show milestones as cards + for m in reversed(milestones[-20:]): + date_str = f"📅 {m['date']}" if m['date'] else "" + st.markdown(f""" +
+ {date_str} +
{m['num']}. {m['title']}
+
+ """, unsafe_allow_html=True) + + with st.expander("📄 View Full History"): + st.markdown(history) + else: + st.error("Could not fetch HISTORY.md") + + with tab2: + st.markdown("## 🔧 Work In Progress") + + if search_query and wip: + highlighted = wip.replace(search_query, f'**:orange[{search_query}]**') + st.markdown(highlighted) + elif wip: + st.markdown(wip) + else: + st.error("Could not fetch WIP.md") + + with tab3: + st.markdown("## 📋 TODO List") + + if search_query and todo: + highlighted = todo.replace(search_query, f'**:yellow[{search_query}]**') + st.markdown(highlighted) + elif todo: + # Parse and display TODO items + lines = todo.split('\n') + completed = 0 + pending = 0 + + for line in lines: + if re.match(r'^- \[x\]', line): + completed += 1 + elif re.match(r'^- \[ \]', line): + pending += 1 + + col1, col2 = st.columns(2) + with col1: + st.metric("✅ Completed", completed) + with col2: + st.metric("⏳ Pending", pending) + + if completed + pending > 0: + progress = completed / (completed + pending) + st.progress(progress, text=f"Progress: {progress*100:.1f}%") + + st.markdown("---") + st.markdown(todo) + else: + st.error("Could not fetch TODO.md") + + with tab4: + st.markdown("## 📖 README") + + if search_query and readme: + highlighted = readme.replace(search_query, f'**:blue[{search_query}]**') + st.markdown(highlighted) + elif readme: + st.markdown(readme) + else: + st.error("Could not fetch README.md") + + with tab5: + st.markdown("## 📈 Evolution Timeline") + + if milestones: + # Group by month + months = {} + for m in milestones: + if m['date']: + month = m['date'][:7] # YYYY-MM + if month not in months: + months[month] = [] + months[month].append(m) + + # Display timeline + for month in sorted(months.keys(), reverse=True): + items = months[month] + month_name = datetime.strptime(month, "%Y-%m").strftime("%B %Y") + + st.markdown(f"### 📅 {month_name}") + + for item in items: + st.markdown(f""" +
+ {item['title']}
+ Milestone #{item['num']} +
+ """, unsafe_allow_html=True) + + # Chart + st.markdown("### 📊 Milestones per Month") + month_counts = {m: len(items) for m, items in months.items()} + if month_counts: + import pandas as pd + df = pd.DataFrame(list(month_counts.items()), columns=['Month', 'Count']) + df = df.sort_values('Month') + st.bar_chart(df.set_index('Month')) + else: + st.info("No dated milestones found in history") + + # Footer + st.markdown("---") + st.markdown(""" +
+ + SecuBox Evolution Dashboard • Auto-synced with GitHub master branch
+ Data refreshes every 5 minutes • + View on GitHub +
+
+ """, unsafe_allow_html=True) + +if __name__ == "__main__": + main()