feat(db): mesh data model — meshes, members, invites, audit log

- pgSchema "mesh" with 4 tables isolating the peer mesh domain
- Enums: visibility, transport, tier, role
- audit_log is metadata-only (E2E encryption enforced at broker/client)
- Cascade on mesh delete, soft-delete via archivedAt/revokedAt

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alejandro Gutiérrez
2026-04-04 21:19:32 +01:00
commit d3163a5bff
1384 changed files with 314925 additions and 0 deletions

276
claude-usage-stats.html Normal file
View File

@@ -0,0 +1,276 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Claude Code Usage Statistics</title>
<script src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
min-height: 100vh;
color: #e0e0e0;
padding: 20px;
}
.container {
max-width: 1400px;
margin: 0 auto;
}
h1 {
text-align: center;
margin-bottom: 10px;
font-size: 2.5rem;
background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.subtitle {
text-align: center;
color: #888;
margin-bottom: 30px;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.stat-card {
background: rgba(255, 255, 255, 0.05);
border-radius: 16px;
padding: 24px;
text-align: center;
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
transition: transform 0.2s;
}
.stat-card:hover {
transform: translateY(-5px);
}
.stat-value {
font-size: 2.5rem;
font-weight: 700;
background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.stat-label {
color: #888;
margin-top: 8px;
font-size: 0.9rem;
}
.chart-container {
background: rgba(255, 255, 255, 0.05);
border-radius: 16px;
padding: 30px;
margin-bottom: 30px;
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
}
.chart-title {
font-size: 1.3rem;
margin-bottom: 20px;
color: #fff;
}
canvas {
max-height: 400px;
}
.footer {
text-align: center;
color: #666;
padding: 20px;
}
</style>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
const { useEffect, useRef } = React;
const USAGE_DATA = [{"date": "2025-09-27", "count": 52}, {"date": "2025-09-28", "count": 135}, {"date": "2025-09-29", "count": 87}, {"date": "2025-09-30", "count": 2}, {"date": "2025-10-02", "count": 20}, {"date": "2025-10-13", "count": 108}, {"date": "2025-10-14", "count": 99}, {"date": "2025-10-15", "count": 3}, {"date": "2025-10-16", "count": 10}, {"date": "2025-10-17", "count": 4}, {"date": "2025-10-21", "count": 4}, {"date": "2025-10-22", "count": 1}, {"date": "2025-10-23", "count": 2}, {"date": "2025-10-24", "count": 12}, {"date": "2025-10-28", "count": 10}, {"date": "2025-10-29", "count": 7}, {"date": "2025-11-03", "count": 10}, {"date": "2025-11-11", "count": 34}, {"date": "2025-11-12", "count": 16}, {"date": "2025-11-23", "count": 3}, {"date": "2025-12-23", "count": 67}, {"date": "2025-12-24", "count": 120}, {"date": "2025-12-25", "count": 272}, {"date": "2025-12-26", "count": 40}, {"date": "2025-12-27", "count": 382}, {"date": "2025-12-28", "count": 244}, {"date": "2025-12-29", "count": 396}, {"date": "2025-12-30", "count": 223}, {"date": "2025-12-31", "count": 215}, {"date": "2026-01-01", "count": 156}, {"date": "2026-01-02", "count": 182}, {"date": "2026-01-03", "count": 141}, {"date": "2026-01-04", "count": 448}, {"date": "2026-01-05", "count": 60}, {"date": "2026-01-06", "count": 160}, {"date": "2026-01-07", "count": 90}, {"date": "2026-01-08", "count": 51}, {"date": "2026-01-09", "count": 1}, {"date": "2026-01-13", "count": 59}, {"date": "2026-01-14", "count": 132}, {"date": "2026-01-15", "count": 4}, {"date": "2026-01-16", "count": 142}, {"date": "2026-01-17", "count": 386}, {"date": "2026-01-18", "count": 649}, {"date": "2026-01-19", "count": 99}, {"date": "2026-01-20", "count": 15}, {"date": "2026-01-21", "count": 160}, {"date": "2026-01-22", "count": 202}, {"date": "2026-01-23", "count": 161}, {"date": "2026-01-24", "count": 433}, {"date": "2026-01-25", "count": 252}, {"date": "2026-01-27", "count": 11}, {"date": "2026-01-28", "count": 177}, {"date": "2026-01-29", "count": 452}, {"date": "2026-01-30", "count": 325}, {"date": "2026-01-31", "count": 484}, {"date": "2026-02-01", "count": 614}, {"date": "2026-02-02", "count": 182}];
function LineChartComponent({ data }) {
const chartRef = useRef(null);
const chartInstance = useRef(null);
useEffect(() => {
if (chartInstance.current) {
chartInstance.current.destroy();
}
const ctx = chartRef.current.getContext('2d');
chartInstance.current = new Chart(ctx, {
type: 'line',
data: {
labels: data.map(d => d.date),
datasets: [{
label: 'Prompts',
data: data.map(d => d.count),
borderColor: '#667eea',
backgroundColor: 'rgba(102, 126, 234, 0.2)',
fill: true,
tension: 0.4,
pointRadius: 3,
pointBackgroundColor: '#667eea'
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: { display: false }
},
scales: {
x: {
ticks: { color: '#888', maxRotation: 45 },
grid: { color: 'rgba(255,255,255,0.1)' }
},
y: {
ticks: { color: '#888' },
grid: { color: 'rgba(255,255,255,0.1)' }
}
}
}
});
return () => {
if (chartInstance.current) {
chartInstance.current.destroy();
}
};
}, [data]);
return <canvas ref={chartRef} height="400"></canvas>;
}
function BarChartComponent({ data }) {
const chartRef = useRef(null);
const chartInstance = useRef(null);
useEffect(() => {
if (chartInstance.current) {
chartInstance.current.destroy();
}
const ctx = chartRef.current.getContext('2d');
chartInstance.current = new Chart(ctx, {
type: 'bar',
data: {
labels: data.map(d => d.week),
datasets: [{
label: 'Prompts',
data: data.map(d => d.count),
backgroundColor: 'rgba(118, 75, 162, 0.7)',
borderColor: '#764ba2',
borderWidth: 1,
borderRadius: 4
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: { display: false }
},
scales: {
x: {
ticks: { color: '#888' },
grid: { color: 'rgba(255,255,255,0.1)' }
},
y: {
ticks: { color: '#888' },
grid: { color: 'rgba(255,255,255,0.1)' }
}
}
}
});
return () => {
if (chartInstance.current) {
chartInstance.current.destroy();
}
};
}, [data]);
return <canvas ref={chartRef} height="300"></canvas>;
}
function App() {
const data = USAGE_DATA;
const totalPrompts = data.reduce((sum, d) => sum + d.count, 0);
const avgPerDay = Math.round(totalPrompts / data.length);
const maxDay = data.reduce((max, d) => d.count > max.count ? d : max, data[0]);
const activeDays = data.filter(d => d.count > 0).length;
// Calculate weekly data
const weeklyData = [];
for (let i = 0; i < data.length; i += 7) {
const weekSlice = data.slice(i, i + 7);
const weekTotal = weekSlice.reduce((sum, d) => sum + d.count, 0);
weeklyData.push({
week: `Week ${Math.floor(i / 7) + 1}`,
count: weekTotal
});
}
return (
<div className="container">
<h1>Claude Code Usage Statistics</h1>
<p className="subtitle">Tracking your AI-assisted development journey</p>
<div className="stats-grid">
<div className="stat-card">
<div className="stat-value">{totalPrompts.toLocaleString()}</div>
<div className="stat-label">Total Prompts</div>
</div>
<div className="stat-card">
<div className="stat-value">{activeDays}</div>
<div className="stat-label">Active Days</div>
</div>
<div className="stat-card">
<div className="stat-value">{avgPerDay}</div>
<div className="stat-label">Avg Per Day</div>
</div>
<div className="stat-card">
<div className="stat-value">{maxDay.count}</div>
<div className="stat-label">Peak Day ({maxDay.date})</div>
</div>
</div>
<div className="chart-container">
<h2 className="chart-title">Daily Usage Over Time</h2>
<div style={{height: '400px'}}>
<LineChartComponent data={data} />
</div>
</div>
<div className="chart-container">
<h2 className="chart-title">Weekly Summary</h2>
<div style={{height: '300px'}}>
<BarChartComponent data={weeklyData} />
</div>
</div>
<div className="footer">
Generated on {new Date().toLocaleDateString()} Data from ~/.claude/history.jsonl
</div>
</div>
);
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>