updated README
This commit is contained in:
parent
99e8f0aaa9
commit
db62e2d92d
2 changed files with 601 additions and 0 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -1 +1,3 @@
|
|||
venv/
|
||||
|
||||
__pycache__/
|
||||
|
|
|
|||
599
README.md
Normal file
599
README.md
Normal file
|
|
@ -0,0 +1,599 @@
|
|||
# 🤖 AgentHub
|
||||
|
||||
**Modern AI Agent Management System with Advanced Analytics**
|
||||
|
||||
AgentHub is a comprehensive, production-ready FastAPI application for managing AI agents across your organization. It provides role-based access control, usage analytics, dual authentication systems, and a modern responsive web interface.
|
||||
|
||||
[](https://fastapi.tiangolo.com/)
|
||||
[](https://www.mongodb.com/)
|
||||
[](https://getbootstrap.com/)
|
||||
[](https://azure.microsoft.com/en-us/services/active-directory/)
|
||||
|
||||
## ✨ Key Features
|
||||
|
||||
### 🔐 **Dual Authentication Systems**
|
||||
- **Local Authentication**: Traditional email/password with bcrypt hashing
|
||||
- **Azure AD Integration**: Enterprise SSO with MSAL and PKCE flow
|
||||
- **JWT Token Management**: Secure cookie-based sessions
|
||||
- **Role-Based Access Control**: Admin and user permissions
|
||||
|
||||
### 🤖 **Comprehensive Agent Management**
|
||||
- **Full CRUD Operations**: Create, read, update, delete AI agents
|
||||
- **Rich Metadata**: Name, description, purpose, version, status, location, department, contact, tags, userbase, capabilities
|
||||
- **Status Tracking**: Active, Development, Inactive, Deprecated
|
||||
- **User Ownership**: Users can only edit their own agents (admins can edit all)
|
||||
- **Advanced Search**: Full-text search across agent fields
|
||||
- **Real-time Filtering**: Filter by status, sort by various criteria
|
||||
|
||||
### 📊 **Usage Analytics & Monitoring**
|
||||
- **Usage Tracking**: Monitor agent usage patterns over time
|
||||
- **Interactive Charts**: Chart.js visualizations with daily/weekly/monthly views
|
||||
- **Usage Statistics**: Total usage, first/last usage tracking
|
||||
- **Date Range Filtering**: Custom date range analysis
|
||||
- **Health Monitoring**: Database connectivity and system health checks
|
||||
|
||||
### 👥 **User Management**
|
||||
- **User Registration**: Self-service account creation
|
||||
- **Admin Interface**: Comprehensive user management for administrators
|
||||
- **Profile Management**: User profile editing and management
|
||||
- **Access Control**: Fine-grained permission management
|
||||
|
||||
### 🎨 **Modern Web Interface**
|
||||
- **Responsive Design**: Mobile-first Bootstrap 5 interface
|
||||
- **Modern UI/UX**: Custom CSS with gradients, animations, and smooth transitions
|
||||
- **Interactive Components**: Modals, dropdowns, tabs, and dynamic content
|
||||
- **Accessibility**: ARIA labels and keyboard navigation support
|
||||
- **Dark Theme Support**: CSS variables for easy theming
|
||||
|
||||
### 🔌 **API Integration**
|
||||
- **RESTful API**: Complete REST API for all operations
|
||||
- **Agent Collector API**: External system integration for automatic agent discovery
|
||||
- **OpenAPI Documentation**: Auto-generated API docs with FastAPI
|
||||
- **Health Endpoints**: System monitoring and diagnostics
|
||||
|
||||
## 🏗️ Architecture
|
||||
|
||||
```
|
||||
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||||
│ Web Interface │ │ REST API │ │ External APIs │
|
||||
│ (Bootstrap 5) │────│ (FastAPI) │────│ (Collector) │
|
||||
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
||||
│ │ │
|
||||
└───────────────────────┼───────────────────────┘
|
||||
│
|
||||
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||||
│ Authentication │ │ Business │ │ Data Layer │
|
||||
│ (JWT + MSAL) │────│ Logic │────│ (MongoDB) │
|
||||
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
||||
```
|
||||
|
||||
### Technology Stack
|
||||
- **Backend**: FastAPI (Python 3.8+)
|
||||
- **Database**: MongoDB with Motor (async driver)
|
||||
- **Authentication**: JWT + Azure AD/MSAL
|
||||
- **Frontend**: Bootstrap 5 + Jinja2 templates
|
||||
- **Charts**: Chart.js for analytics
|
||||
- **Deployment**: Uvicorn + Systemd
|
||||
- **Security**: bcrypt, python-jose, CSRF protection
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
1. **Clone and Install**
|
||||
```bash
|
||||
git clone <repository-url>
|
||||
cd agent_app
|
||||
python -m venv venv
|
||||
source venv/bin/activate # Windows: venv\Scripts\activate
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
2. **Configure Environment**
|
||||
```bash
|
||||
cp .env.example .env
|
||||
# Edit .env with your configuration
|
||||
```
|
||||
|
||||
3. **Start MongoDB**
|
||||
```bash
|
||||
# macOS with Homebrew
|
||||
brew services start mongodb-community
|
||||
|
||||
# Linux with systemd
|
||||
sudo systemctl start mongod
|
||||
```
|
||||
|
||||
4. **Run Application**
|
||||
```bash
|
||||
uvicorn main:app --reload --port 8000
|
||||
```
|
||||
|
||||
5. **Access Application**
|
||||
- Visit: http://localhost:8000
|
||||
- Default admin: `admin@agenthub.com` / `admin123`
|
||||
- Test user: `test@example.com` / `testpass123`
|
||||
|
||||
## 📋 Installation & Setup
|
||||
|
||||
### Prerequisites
|
||||
- Python 3.8+
|
||||
- MongoDB 4.4+
|
||||
- Git
|
||||
|
||||
### Local Development Setup
|
||||
|
||||
1. **Environment Setup**
|
||||
```bash
|
||||
python -m venv venv
|
||||
source venv/bin/activate
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
2. **Environment Configuration**
|
||||
Create `.env` file:
|
||||
```env
|
||||
# Database
|
||||
MONGODB_URI=mongodb://localhost:27017
|
||||
MONGODB_DBNAME=agenthub_db
|
||||
|
||||
# Security
|
||||
SECRET_KEY=your-secret-key-here
|
||||
ALGORITHM=HS256
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES=60
|
||||
|
||||
# Local Development
|
||||
DISABLE_MSAL=true
|
||||
BASE_PATH=
|
||||
|
||||
# Agent Collector API
|
||||
AGENT_COLLECTOR_API_KEY=your-api-key-here
|
||||
```
|
||||
|
||||
3. **Database Setup**
|
||||
- Install and start MongoDB locally
|
||||
- No manual database creation needed (auto-created)
|
||||
|
||||
4. **Create Admin User**
|
||||
```bash
|
||||
# Register via web interface first, then promote
|
||||
python make_admin.py admin@yourdomain.com
|
||||
```
|
||||
|
||||
### Production Setup
|
||||
|
||||
1. **Azure AD Configuration** (Optional)
|
||||
```env
|
||||
DISABLE_MSAL=false
|
||||
AZURE_CLIENT_ID=your-client-id
|
||||
AZURE_AUTHORITY=https://login.microsoftonline.com/your-tenant-id
|
||||
AZURE_REDIRECT_URI=https://your-domain.com/auth/azure/callback
|
||||
BASE_PATH=/your-app-path
|
||||
```
|
||||
|
||||
2. **Systemd Service**
|
||||
```bash
|
||||
sudo cp agenthub.service /etc/systemd/system/
|
||||
sudo systemctl enable agenthub
|
||||
sudo systemctl start agenthub
|
||||
```
|
||||
|
||||
## 🎯 Usage
|
||||
|
||||
### Agent Management
|
||||
1. **Create Agent**: Navigate to "Register New Agent" and fill in details
|
||||
2. **View Agents**: Use "All Agents" or "My Agents" views
|
||||
3. **Search & Filter**: Use the search bar and status filters
|
||||
4. **Edit/Delete**: Use action buttons (only for owned agents)
|
||||
5. **Usage Analytics**: View detailed usage charts and statistics
|
||||
|
||||
### User Management (Admin)
|
||||
1. **Access Admin Dashboard**: Admin users see admin navigation
|
||||
2. **Manage Users**: View, edit, activate/deactivate users
|
||||
3. **Promote Users**: Grant admin privileges to users
|
||||
4. **Monitor System**: View statistics and system health
|
||||
|
||||
### Search & Discovery
|
||||
- **Global Search**: Search across agents and users
|
||||
- **Advanced Filtering**: Filter by status, department, tags
|
||||
- **Real-time Results**: Instant search as you type
|
||||
|
||||
## 📚 API Documentation
|
||||
|
||||
### Authentication Endpoints
|
||||
```
|
||||
POST /login # Local authentication
|
||||
GET /auth/azure/login # Azure AD login (if enabled)
|
||||
GET /auth/azure/callback # Azure AD callback
|
||||
GET /logout # Logout and clear session
|
||||
```
|
||||
|
||||
### Agent API Endpoints
|
||||
```
|
||||
GET /api/agents # Get user's agents
|
||||
GET /api/agents/all # Get all agents (for users)
|
||||
POST /api/agents # Create new agent
|
||||
GET /api/agents/{id} # Get specific agent
|
||||
PUT /api/agents/{id} # Update agent
|
||||
DELETE /api/agents/{id} # Delete agent
|
||||
```
|
||||
|
||||
### Usage Analytics API
|
||||
```
|
||||
GET /api/agents/{name}/usage # Get usage statistics
|
||||
GET /api/agents/{name}/usage/chart # Get chart data
|
||||
```
|
||||
|
||||
### Admin API Endpoints
|
||||
```
|
||||
GET /api/admin/users # Get all users
|
||||
PUT /api/admin/users/{email} # Update user
|
||||
GET /api/admin/agents # Get all agents
|
||||
```
|
||||
|
||||
### Agent Collector API
|
||||
```
|
||||
POST /agents # External agent registration
|
||||
GET / # Health check
|
||||
```
|
||||
|
||||
### API Usage Examples
|
||||
|
||||
**Create Agent**
|
||||
```bash
|
||||
curl -X POST "http://localhost:8000/api/agents" \
|
||||
-H "Content-Type: application/json" \
|
||||
-b "access_token=your-jwt-token" \
|
||||
-d '{
|
||||
"agent_name": "DataProcessor",
|
||||
"agent_description": "Processes customer data",
|
||||
"agent_purpose": "Data analysis and reporting",
|
||||
"agent_status": "Active",
|
||||
"agent_version": "1.2.0",
|
||||
"agent_department": "Analytics",
|
||||
"agent_capabilities": ["data-processing", "reporting"]
|
||||
}'
|
||||
```
|
||||
|
||||
**Get Usage Analytics**
|
||||
```bash
|
||||
curl "http://localhost:8000/api/agents/DataProcessor/usage?start_date=2024-01-01&end_date=2024-12-31" \
|
||||
-b "access_token=your-jwt-token"
|
||||
```
|
||||
|
||||
## 🔧 Configuration
|
||||
|
||||
### Environment Variables
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `MONGODB_URI` | `mongodb://localhost:27017` | MongoDB connection string |
|
||||
| `MONGODB_DBNAME` | `agenthub_db` | Database name |
|
||||
| `SECRET_KEY` | *required* | JWT secret key |
|
||||
| `ALGORITHM` | `HS256` | JWT algorithm |
|
||||
| `ACCESS_TOKEN_EXPIRE_MINUTES` | `60` | Token expiration time |
|
||||
| `DISABLE_MSAL` | `false` | Disable Azure AD authentication |
|
||||
| `BASE_PATH` | `` | Application base path for reverse proxy |
|
||||
| `AZURE_CLIENT_ID` | *optional* | Azure AD application ID |
|
||||
| `AZURE_AUTHORITY` | *optional* | Azure AD authority URL |
|
||||
| `AZURE_REDIRECT_URI` | *optional* | Azure AD callback URL |
|
||||
| `AGENT_COLLECTOR_API_KEY` | *optional* | API key for collector endpoints |
|
||||
|
||||
### Authentication Modes
|
||||
|
||||
**Local Only** (Development)
|
||||
```env
|
||||
DISABLE_MSAL=true
|
||||
BASE_PATH=
|
||||
```
|
||||
|
||||
**Azure AD + Local** (Production)
|
||||
```env
|
||||
DISABLE_MSAL=false
|
||||
BASE_PATH=/your-app-path
|
||||
AZURE_CLIENT_ID=your-client-id
|
||||
AZURE_AUTHORITY=https://login.microsoftonline.com/your-tenant
|
||||
AZURE_REDIRECT_URI=https://your-domain.com/your-app-path/auth/azure/callback
|
||||
```
|
||||
|
||||
## 🛠️ Development
|
||||
|
||||
### Project Structure
|
||||
```
|
||||
agent_app/
|
||||
├── main.py # FastAPI application and routes
|
||||
├── models.py # Pydantic data models
|
||||
├── crud.py # Database operations (CRUD)
|
||||
├── auth.py # JWT authentication logic
|
||||
├── database.py # MongoDB connection setup
|
||||
├── config.py # Configuration management
|
||||
├── msal_auth.py # Azure AD/MSAL authentication
|
||||
├── make_admin.py # Admin user promotion script
|
||||
├── requirements.txt # Python dependencies
|
||||
├── agenthub.service # Systemd service configuration
|
||||
├── templates/ # Jinja2 HTML templates
|
||||
│ ├── base.html # Base template with Bootstrap
|
||||
│ ├── nav.html # Dynamic navigation component
|
||||
│ ├── index.html # Landing page
|
||||
│ ├── login.html # Authentication form
|
||||
│ ├── register.html # User registration
|
||||
│ ├── agent_register.html # Agent creation form
|
||||
│ ├── agent_management.html # Agent dashboard
|
||||
│ ├── search.html # Global search interface
|
||||
│ ├── profile.html # User profile management
|
||||
│ ├── user_management.html # User management interface
|
||||
│ └── admin/
|
||||
│ └── dashboard.html # Admin dashboard with analytics
|
||||
├── static/ # Static assets
|
||||
│ ├── style.css # Custom CSS with modern design
|
||||
│ └── microsoft-logo.svg # Microsoft branding asset
|
||||
└── documentation/
|
||||
├── CLAUDE.md # Claude Code development guidelines
|
||||
├── README_DEV.md # Local development setup guide
|
||||
├── FEATURE_SUMMARY.md # Detailed feature breakdown
|
||||
└── agent_collector_api_documentation.md # API integration guide
|
||||
```
|
||||
|
||||
### Development Guidelines
|
||||
|
||||
1. **Code Style**: Follow existing patterns and conventions
|
||||
2. **Database**: Use Motor async driver with proper ObjectId handling
|
||||
3. **Authentication**: Use cookie-based auth for web interface
|
||||
4. **Templates**: Pass `current_user` dict objects to templates
|
||||
5. **Error Handling**: Implement comprehensive error handling
|
||||
6. **Security**: Never log or expose secrets, validate all inputs
|
||||
|
||||
### Running Tests
|
||||
```bash
|
||||
# Add your test commands here when implemented
|
||||
pytest tests/
|
||||
```
|
||||
|
||||
### Code Quality
|
||||
```bash
|
||||
# Add linting commands here when implemented
|
||||
flake8 .
|
||||
black .
|
||||
```
|
||||
|
||||
## 🚀 Deployment
|
||||
|
||||
### Production Deployment
|
||||
|
||||
1. **Server Setup**
|
||||
```bash
|
||||
# Install dependencies
|
||||
apt update && apt install python3 python3-venv mongodb
|
||||
|
||||
# Create application directory
|
||||
mkdir -p /var/www/html/agent_tracker
|
||||
cd /var/www/html/agent_tracker
|
||||
```
|
||||
|
||||
2. **Application Setup**
|
||||
```bash
|
||||
# Deploy application files
|
||||
git clone <repository> .
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
3. **Service Configuration**
|
||||
```bash
|
||||
# Install systemd service
|
||||
sudo cp agenthub.service /etc/systemd/system/
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable agenthub
|
||||
sudo systemctl start agenthub
|
||||
```
|
||||
|
||||
4. **Nginx Configuration** (Example)
|
||||
```nginx
|
||||
location /agent_tracker {
|
||||
proxy_pass http://localhost:8038;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
```
|
||||
|
||||
### Docker Deployment
|
||||
```dockerfile
|
||||
FROM python:3.11-slim
|
||||
|
||||
WORKDIR /app
|
||||
COPY requirements.txt .
|
||||
RUN pip install -r requirements.txt
|
||||
|
||||
COPY . .
|
||||
EXPOSE 8000
|
||||
|
||||
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||
```
|
||||
|
||||
### Environment-Specific Configuration
|
||||
|
||||
**Development**: Local auth only, root path
|
||||
**Staging**: Azure AD + local, sub-path
|
||||
**Production**: Azure AD primary, sub-path, enhanced security
|
||||
|
||||
## 🔌 External Integrations
|
||||
|
||||
### Agent Collector API
|
||||
|
||||
External systems can automatically register agents using the Agent Collector API:
|
||||
|
||||
```python
|
||||
import requests
|
||||
|
||||
# Register agent from external system
|
||||
agent_data = {
|
||||
"name": "AutomationBot",
|
||||
"description": "Automated task processing agent",
|
||||
"purpose": "Process and analyze customer requests",
|
||||
"status": "active",
|
||||
"department": "Customer Success",
|
||||
"capabilities": ["nlp", "automation", "data-analysis"]
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
"https://your-domain.com/agents",
|
||||
headers={
|
||||
"Content-Type": "application/json",
|
||||
"X-API-Key": "your-api-key"
|
||||
},
|
||||
json=agent_data
|
||||
)
|
||||
```
|
||||
|
||||
**Features**:
|
||||
- Automatic agent registration
|
||||
- Duplicate detection and usage tracking
|
||||
- Health monitoring integration
|
||||
- Error handling and retry logic
|
||||
|
||||
## 📈 Analytics & Monitoring
|
||||
|
||||
### Usage Tracking
|
||||
- **Automatic Collection**: Usage data collected via Agent Collector API
|
||||
- **Visualization**: Interactive charts with multiple time periods
|
||||
- **Statistics**: Total usage, trends, peak periods
|
||||
- **Export**: Data available via REST API
|
||||
|
||||
### System Monitoring
|
||||
- **Health Checks**: Database connectivity monitoring
|
||||
- **Performance**: Async operations for scalability
|
||||
- **Logging**: Comprehensive application logging
|
||||
- **Metrics**: User and agent statistics
|
||||
|
||||
## 🔒 Security
|
||||
|
||||
### Authentication Security
|
||||
- **Password Hashing**: bcrypt with salt
|
||||
- **JWT Tokens**: Secure token generation with expiration
|
||||
- **CSRF Protection**: State validation for OAuth flows
|
||||
- **Session Management**: Secure cookie handling
|
||||
|
||||
### Application Security
|
||||
- **Input Validation**: Pydantic model validation
|
||||
- **SQL Injection**: MongoDB queries with proper escaping
|
||||
- **XSS Protection**: Template auto-escaping
|
||||
- **HTTPS**: TLS encryption in production
|
||||
|
||||
### Access Control
|
||||
- **Role-Based**: Admin vs user permissions
|
||||
- **Resource Ownership**: Users can only access their own resources
|
||||
- **API Authentication**: Token-based API access
|
||||
- **Admin Functions**: Protected admin-only operations
|
||||
|
||||
## 🤝 Contributing
|
||||
|
||||
1. **Fork the Repository**
|
||||
2. **Create Feature Branch**: `git checkout -b feature/amazing-feature`
|
||||
3. **Make Changes**: Follow existing code patterns
|
||||
4. **Test Changes**: Ensure all functionality works
|
||||
5. **Commit Changes**: `git commit -m 'Add amazing feature'`
|
||||
6. **Push Branch**: `git push origin feature/amazing-feature`
|
||||
7. **Open Pull Request**
|
||||
|
||||
### Development Workflow
|
||||
- Use virtual environments for isolation
|
||||
- Follow existing code style and patterns
|
||||
- Test all authentication flows
|
||||
- Verify responsive design
|
||||
- Check admin and user permissions
|
||||
|
||||
## 📝 Common Tasks
|
||||
|
||||
### User Management
|
||||
```bash
|
||||
# Create admin user
|
||||
python make_admin.py user@domain.com
|
||||
|
||||
# Check user status
|
||||
python -c "import asyncio; import crud; print(asyncio.run(crud.get_user_by_email('user@domain.com')))"
|
||||
```
|
||||
|
||||
### Database Operations
|
||||
```bash
|
||||
# Access MongoDB directly
|
||||
mongosh agenthub_db
|
||||
|
||||
# View collections
|
||||
db.agents.find()
|
||||
db.users.find()
|
||||
db.agent_usage.find()
|
||||
```
|
||||
|
||||
### Service Management
|
||||
```bash
|
||||
# Check service status
|
||||
sudo systemctl status agenthub
|
||||
|
||||
# View logs
|
||||
sudo journalctl -u agenthub -f
|
||||
|
||||
# Restart service
|
||||
sudo systemctl restart agenthub
|
||||
```
|
||||
|
||||
## 🐛 Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
**Authentication Problems**
|
||||
- Check JWT secret key configuration
|
||||
- Verify Azure AD credentials (if using MSAL)
|
||||
- Clear browser cookies and retry
|
||||
|
||||
**Database Connection**
|
||||
- Verify MongoDB is running: `sudo systemctl status mongod`
|
||||
- Check connection string in environment variables
|
||||
- Validate database permissions
|
||||
|
||||
**Template Rendering**
|
||||
- Ensure `BASE_PATH` is correctly configured
|
||||
- Check static file serving configuration
|
||||
- Verify template context variables
|
||||
|
||||
**Permission Errors**
|
||||
- Check user roles in database
|
||||
- Verify admin permissions for management functions
|
||||
- Review agent ownership for edit/delete operations
|
||||
|
||||
### Debug Mode
|
||||
```bash
|
||||
# Enable debug logging
|
||||
export DEBUG=true
|
||||
uvicorn main:app --reload --log-level debug
|
||||
```
|
||||
|
||||
## 📊 System Requirements
|
||||
|
||||
### Minimum Requirements
|
||||
- **CPU**: 1 core
|
||||
- **RAM**: 512MB
|
||||
- **Storage**: 1GB
|
||||
- **Network**: 1Mbps
|
||||
|
||||
### Recommended Production
|
||||
- **CPU**: 2+ cores
|
||||
- **RAM**: 2GB+
|
||||
- **Storage**: 10GB+ (for logs and data)
|
||||
- **Network**: 10Mbps+
|
||||
- **Database**: MongoDB cluster or replica set
|
||||
|
||||
## 📄 License
|
||||
|
||||
This project is licensed under the MIT License - see the LICENSE file for details.
|
||||
|
||||
## 🆘 Support
|
||||
|
||||
- **Documentation**: Check `/docs` endpoint for API documentation
|
||||
- **Issues**: Report bugs and feature requests via GitHub issues
|
||||
- **Health Check**: Visit `/debug/config` for system configuration
|
||||
- **Admin Tools**: Use admin dashboard for system monitoring
|
||||
|
||||
---
|
||||
|
||||
**AgentHub** - Built with ❤️ using FastAPI, MongoDB, and Bootstrap 5
|
||||
Loading…
Add table
Reference in a new issue