Fix backend: add passlib/bcrypt, fix datetime timezone issues
This commit is contained in:
@@ -1,11 +1,15 @@
|
|||||||
# Troubleshooting Decision Tree Application
|
# Troubleshooting Decision Tree Application
|
||||||
|
|
||||||
## Project Overview
|
## Project Overview
|
||||||
|
|
||||||
### Vision
|
### Vision
|
||||||
|
|
||||||
A decision tree troubleshooting application designed for MSP engineers to transform diagnostic processes into clean, professional documentation automatically.
|
A decision tree troubleshooting application designed for MSP engineers to transform diagnostic processes into clean, professional documentation automatically.
|
||||||
|
|
||||||
### Core Problem
|
### Core Problem
|
||||||
|
|
||||||
MSP engineers like Michael face constant context switching between diverse technical issues (file shares, server outages, VPN failures, Active Directory problems). Each context switch:
|
MSP engineers like Michael face constant context switching between diverse technical issues (file shares, server outages, VPN failures, Active Directory problems). Each context switch:
|
||||||
|
|
||||||
- Takes 15-25 minutes to regain full focus
|
- Takes 15-25 minutes to regain full focus
|
||||||
- Creates cognitive overhead and attention residue
|
- Creates cognitive overhead and attention residue
|
||||||
- Contributes to burnout (research-backed)
|
- Contributes to burnout (research-backed)
|
||||||
@@ -13,7 +17,9 @@ MSP engineers like Michael face constant context switching between diverse techn
|
|||||||
- Results in lost tribal knowledge
|
- Results in lost tribal knowledge
|
||||||
|
|
||||||
### Solution
|
### Solution
|
||||||
|
|
||||||
An intelligent decision tree system that:
|
An intelligent decision tree system that:
|
||||||
|
|
||||||
- Guides engineers through proven troubleshooting paths
|
- Guides engineers through proven troubleshooting paths
|
||||||
- Captures decisions and notes automatically
|
- Captures decisions and notes automatically
|
||||||
- Generates professional ticket documentation
|
- Generates professional ticket documentation
|
||||||
@@ -21,9 +27,11 @@ An intelligent decision tree system that:
|
|||||||
- Reduces cognitive load during high-stress situations
|
- Reduces cognitive load during high-stress situations
|
||||||
|
|
||||||
### Success Criteria
|
### Success Criteria
|
||||||
|
|
||||||
**3-Month Goal:** Michael uses this tool for 50% of his tickets
|
**3-Month Goal:** Michael uses this tool for 50% of his tickets
|
||||||
|
|
||||||
**Key Metrics:**
|
**Key Metrics:**
|
||||||
|
|
||||||
- Time saved per ticket
|
- Time saved per ticket
|
||||||
- Documentation quality improvement
|
- Documentation quality improvement
|
||||||
- Reduction in "what did I do?" moments
|
- Reduction in "what did I do?" moments
|
||||||
@@ -31,14 +39,17 @@ An intelligent decision tree system that:
|
|||||||
- Reduction in repeated troubleshooting attempts
|
- Reduction in repeated troubleshooting attempts
|
||||||
|
|
||||||
### Target Users
|
### Target Users
|
||||||
|
|
||||||
**Primary:** Senior Systems Engineers at MSPs managing Windows Server, Active Directory, Citrix, networking equipment
|
**Primary:** Senior Systems Engineers at MSPs managing Windows Server, Active Directory, Citrix, networking equipment
|
||||||
|
|
||||||
**Secondary:**
|
**Secondary:**
|
||||||
|
|
||||||
- Junior engineers needing guided troubleshooting
|
- Junior engineers needing guided troubleshooting
|
||||||
- Onsite technicians following remote engineer instructions
|
- Onsite technicians following remote engineer instructions
|
||||||
- MSP teams wanting standardized procedures
|
- MSP teams wanting standardized procedures
|
||||||
|
|
||||||
### Key Differentiators
|
### Key Differentiators
|
||||||
|
|
||||||
1. **Automatic documentation generation** - No separate note-taking step
|
1. **Automatic documentation generation** - No separate note-taking step
|
||||||
2. **On-the-fly customization** - Add custom branches when encountering edge cases
|
2. **On-the-fly customization** - Add custom branches when encountering edge cases
|
||||||
3. **Learning system** - Tracks common paths, suggests optimizations
|
3. **Learning system** - Tracks common paths, suggests optimizations
|
||||||
@@ -47,11 +58,13 @@ An intelligent decision tree system that:
|
|||||||
6. **Team collaboration** - Controlled authorship with shared access
|
6. **Team collaboration** - Controlled authorship with shared access
|
||||||
|
|
||||||
### Potential Market
|
### Potential Market
|
||||||
|
|
||||||
- 30,000+ MSPs in North America alone
|
- 30,000+ MSPs in North America alone
|
||||||
- Average MSP has 15-50 technical staff
|
- Average MSP has 15-50 technical staff
|
||||||
- Adjacent markets: Internal IT teams, DevOps, Technical Support
|
- Adjacent markets: Internal IT teams, DevOps, Technical Support
|
||||||
|
|
||||||
### Monetization Possibilities
|
### Monetization Possibilities
|
||||||
|
|
||||||
- **Free Tier:** Personal use, limited trees
|
- **Free Tier:** Personal use, limited trees
|
||||||
- **Pro Tier:** Team sharing, unlimited trees, analytics
|
- **Pro Tier:** Team sharing, unlimited trees, analytics
|
||||||
- **Enterprise:** API access, SSO, custom branding, white-label
|
- **Enterprise:** API access, SSO, custom branding, white-label
|
||||||
@@ -60,6 +73,7 @@ An intelligent decision tree system that:
|
|||||||
- **Integrations:** Paid add-ons for ConnectWise, Kaseya, etc.
|
- **Integrations:** Paid add-ons for ConnectWise, Kaseya, etc.
|
||||||
|
|
||||||
### Name Ideas (To Workshop)
|
### Name Ideas (To Workshop)
|
||||||
|
|
||||||
- TroubleTree
|
- TroubleTree
|
||||||
- DecisionPath
|
- DecisionPath
|
||||||
- MSP Navigator
|
- MSP Navigator
|
||||||
@@ -71,7 +85,9 @@ An intelligent decision tree system that:
|
|||||||
- DiagPath
|
- DiagPath
|
||||||
|
|
||||||
### Competitive Landscape
|
### Competitive Landscape
|
||||||
|
|
||||||
**Current Solutions:**
|
**Current Solutions:**
|
||||||
|
|
||||||
- Static runbooks/wiki pages (not interactive)
|
- Static runbooks/wiki pages (not interactive)
|
||||||
- Flowchart tools (not designed for real-time troubleshooting)
|
- Flowchart tools (not designed for real-time troubleshooting)
|
||||||
- Ticketing system templates (limited branching logic)
|
- Ticketing system templates (limited branching logic)
|
||||||
@@ -81,6 +97,7 @@ An intelligent decision tree system that:
|
|||||||
Purpose-built for technical troubleshooting with automation integration and automatic documentation generation.
|
Purpose-built for technical troubleshooting with automation integration and automatic documentation generation.
|
||||||
|
|
||||||
### Technology Philosophy
|
### Technology Philosophy
|
||||||
|
|
||||||
- **Web-first:** Accessible anywhere, no installation
|
- **Web-first:** Accessible anywhere, no installation
|
||||||
- **Progressive enhancement:** Works offline, syncs when online
|
- **Progressive enhancement:** Works offline, syncs when online
|
||||||
- **API-driven:** Backend separate from frontend for flexibility
|
- **API-driven:** Backend separate from frontend for flexibility
|
||||||
@@ -88,6 +105,7 @@ Purpose-built for technical troubleshooting with automation integration and auto
|
|||||||
- **Open-source friendly:** Consider open-sourcing core, monetize integrations/hosting
|
- **Open-source friendly:** Consider open-sourcing core, monetize integrations/hosting
|
||||||
|
|
||||||
### Project Status
|
### Project Status
|
||||||
|
|
||||||
**Current Phase:** Planning and Architecture
|
**Current Phase:** Planning and Architecture
|
||||||
**Next Phase:** MVP Development (Weeks 1-3)
|
**Next Phase:** MVP Development (Weeks 1-3)
|
||||||
**Target MVP Date:** 3 weeks from project start
|
**Target MVP Date:** 3 weeks from project start
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
## System Architecture
|
## System Architecture
|
||||||
|
|
||||||
### High-Level Architecture
|
### High-Level Architecture
|
||||||
|
|
||||||
```
|
```
|
||||||
┌─────────────────────────────────────────────────────┐
|
┌─────────────────────────────────────────────────────┐
|
||||||
│ Frontend (React/Vue) │
|
│ Frontend (React/Vue) │
|
||||||
@@ -41,7 +42,9 @@
|
|||||||
## Tech Stack
|
## Tech Stack
|
||||||
|
|
||||||
### Frontend
|
### Frontend
|
||||||
|
|
||||||
**Primary Choice: React**
|
**Primary Choice: React**
|
||||||
|
|
||||||
- **Pros:** Large ecosystem, excellent offline support (PWA), familiar to most developers
|
- **Pros:** Large ecosystem, excellent offline support (PWA), familiar to most developers
|
||||||
- **Alternatives:** Vue.js (simpler), Svelte (faster)
|
- **Alternatives:** Vue.js (simpler), Svelte (faster)
|
||||||
- **UI Framework:** Tailwind CSS + shadcn/ui (clean, professional look)
|
- **UI Framework:** Tailwind CSS + shadcn/ui (clean, professional look)
|
||||||
@@ -50,7 +53,9 @@
|
|||||||
- **Offline:** Service Workers + IndexedDB for offline tree caching
|
- **Offline:** Service Workers + IndexedDB for offline tree caching
|
||||||
|
|
||||||
### Backend
|
### Backend
|
||||||
|
|
||||||
**Primary Choice: Python FastAPI**
|
**Primary Choice: Python FastAPI**
|
||||||
|
|
||||||
- **Pros:** Modern, fast, async support, automatic API docs, matches Michael's learning path
|
- **Pros:** Modern, fast, async support, automatic API docs, matches Michael's learning path
|
||||||
- **Alternatives:** Flask (simpler but less performant), Django (heavier)
|
- **Alternatives:** Flask (simpler but less performant), Django (heavier)
|
||||||
- **Authentication:** JWT tokens + httpOnly cookies
|
- **Authentication:** JWT tokens + httpOnly cookies
|
||||||
@@ -59,7 +64,9 @@
|
|||||||
- **Migration:** Alembic
|
- **Migration:** Alembic
|
||||||
|
|
||||||
### Database
|
### Database
|
||||||
|
|
||||||
**Primary Choice: PostgreSQL**
|
**Primary Choice: PostgreSQL**
|
||||||
|
|
||||||
- **Pros:** JSON/JSONB support perfect for tree storage, reliable, scalable
|
- **Pros:** JSON/JSONB support perfect for tree storage, reliable, scalable
|
||||||
- **Schema Design:**
|
- **Schema Design:**
|
||||||
- Hybrid approach: Relational for users/sessions, JSONB for tree structure
|
- Hybrid approach: Relational for users/sessions, JSONB for tree structure
|
||||||
@@ -67,18 +74,23 @@
|
|||||||
- Indexes on frequently queried fields
|
- Indexes on frequently queried fields
|
||||||
|
|
||||||
### File Storage
|
### File Storage
|
||||||
|
|
||||||
**Primary Choice: S3-compatible storage**
|
**Primary Choice: S3-compatible storage**
|
||||||
|
|
||||||
- **Development:** MinIO (self-hosted, S3-compatible)
|
- **Development:** MinIO (self-hosted, S3-compatible)
|
||||||
- **Production:** AWS S3 or DigitalOcean Spaces
|
- **Production:** AWS S3 or DigitalOcean Spaces
|
||||||
- **Strategy:** Pre-signed URLs for uploads, CDN for delivery
|
- **Strategy:** Pre-signed URLs for uploads, CDN for delivery
|
||||||
|
|
||||||
### Hosting
|
### Hosting
|
||||||
|
|
||||||
**Development:**
|
**Development:**
|
||||||
|
|
||||||
- Frontend: Local dev server (Vite)
|
- Frontend: Local dev server (Vite)
|
||||||
- Backend: Local Python server
|
- Backend: Local Python server
|
||||||
- Database: Docker PostgreSQL
|
- Database: Docker PostgreSQL
|
||||||
|
|
||||||
**Production Options:**
|
**Production Options:**
|
||||||
|
|
||||||
1. **Simple Start:** Railway or Render (full-stack hosting)
|
1. **Simple Start:** Railway or Render (full-stack hosting)
|
||||||
- Cost: ~$10-20/month
|
- Cost: ~$10-20/month
|
||||||
- Pros: Easy deployment, managed databases
|
- Pros: Easy deployment, managed databases
|
||||||
@@ -99,6 +111,7 @@
|
|||||||
### Database Schema
|
### Database Schema
|
||||||
|
|
||||||
#### Users Table
|
#### Users Table
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
CREATE TABLE users (
|
CREATE TABLE users (
|
||||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
@@ -113,6 +126,7 @@ CREATE TABLE users (
|
|||||||
```
|
```
|
||||||
|
|
||||||
#### Teams Table
|
#### Teams Table
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
CREATE TABLE teams (
|
CREATE TABLE teams (
|
||||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
@@ -122,6 +136,7 @@ CREATE TABLE teams (
|
|||||||
```
|
```
|
||||||
|
|
||||||
#### Trees Table
|
#### Trees Table
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
CREATE TABLE trees (
|
CREATE TABLE trees (
|
||||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
@@ -144,6 +159,7 @@ CREATE INDEX idx_trees_search ON trees USING gin(to_tsvector('english', name ||
|
|||||||
```
|
```
|
||||||
|
|
||||||
#### Sessions Table
|
#### Sessions Table
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
CREATE TABLE sessions (
|
CREATE TABLE sessions (
|
||||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
@@ -165,6 +181,7 @@ CREATE INDEX idx_sessions_dates ON sessions(started_at, completed_at);
|
|||||||
```
|
```
|
||||||
|
|
||||||
#### Attachments Table
|
#### Attachments Table
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
CREATE TABLE attachments (
|
CREATE TABLE attachments (
|
||||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
@@ -368,6 +385,7 @@ CREATE TABLE attachments (
|
|||||||
## API Endpoints
|
## API Endpoints
|
||||||
|
|
||||||
### Authentication
|
### Authentication
|
||||||
|
|
||||||
```
|
```
|
||||||
POST /api/auth/register - Register new user
|
POST /api/auth/register - Register new user
|
||||||
POST /api/auth/login - Login
|
POST /api/auth/login - Login
|
||||||
@@ -377,6 +395,7 @@ POST /api/auth/refresh - Refresh JWT token
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Trees
|
### Trees
|
||||||
|
|
||||||
```
|
```
|
||||||
GET /api/trees - List all trees (with filters)
|
GET /api/trees - List all trees (with filters)
|
||||||
GET /api/trees/:id - Get specific tree
|
GET /api/trees/:id - Get specific tree
|
||||||
@@ -388,6 +407,7 @@ GET /api/trees/search - Full-text search trees
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Sessions
|
### Sessions
|
||||||
|
|
||||||
```
|
```
|
||||||
GET /api/sessions - List user's sessions
|
GET /api/sessions - List user's sessions
|
||||||
GET /api/sessions/:id - Get specific session
|
GET /api/sessions/:id - Get specific session
|
||||||
@@ -398,6 +418,7 @@ POST /api/sessions/:id/export - Export session to formatted notes
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Attachments
|
### Attachments
|
||||||
|
|
||||||
```
|
```
|
||||||
POST /api/sessions/:id/attachments - Upload attachment
|
POST /api/sessions/:id/attachments - Upload attachment
|
||||||
GET /api/sessions/:id/attachments - List attachments
|
GET /api/sessions/:id/attachments - List attachments
|
||||||
@@ -406,6 +427,7 @@ DELETE /api/attachments/:id - Delete attachment
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Teams (Phase 2)
|
### Teams (Phase 2)
|
||||||
|
|
||||||
```
|
```
|
||||||
GET /api/teams - List teams
|
GET /api/teams - List teams
|
||||||
POST /api/teams - Create team (admin only)
|
POST /api/teams - Create team (admin only)
|
||||||
@@ -415,6 +437,7 @@ DELETE /api/teams/:id/members/:user_id - Remove team member
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Analytics (Phase 3)
|
### Analytics (Phase 3)
|
||||||
|
|
||||||
```
|
```
|
||||||
GET /api/analytics/trees/:id/usage - Tree usage statistics
|
GET /api/analytics/trees/:id/usage - Tree usage statistics
|
||||||
GET /api/analytics/trees/:id/paths - Common paths taken
|
GET /api/analytics/trees/:id/paths - Common paths taken
|
||||||
@@ -423,6 +446,7 @@ GET /api/analytics/user/history - User's troubleshooting history
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Automation (Phase 4)
|
### Automation (Phase 4)
|
||||||
|
|
||||||
```
|
```
|
||||||
GET /api/automation/scripts - List available automation scripts
|
GET /api/automation/scripts - List available automation scripts
|
||||||
POST /api/automation/execute - Execute automation script
|
POST /api/automation/execute - Execute automation script
|
||||||
@@ -432,6 +456,7 @@ GET /api/automation/history - Automation execution history
|
|||||||
## Security Considerations
|
## Security Considerations
|
||||||
|
|
||||||
### Authentication & Authorization
|
### Authentication & Authorization
|
||||||
|
|
||||||
- JWT tokens with short expiry (15 min access, 7 day refresh)
|
- JWT tokens with short expiry (15 min access, 7 day refresh)
|
||||||
- Role-based access control (RBAC)
|
- Role-based access control (RBAC)
|
||||||
- Password requirements: min 10 chars, complexity
|
- Password requirements: min 10 chars, complexity
|
||||||
@@ -439,6 +464,7 @@ GET /api/automation/history - Automation execution history
|
|||||||
- Account lockout after failed attempts
|
- Account lockout after failed attempts
|
||||||
|
|
||||||
### Data Protection
|
### Data Protection
|
||||||
|
|
||||||
- All passwords hashed with bcrypt (cost factor 12)
|
- All passwords hashed with bcrypt (cost factor 12)
|
||||||
- Sensitive data encrypted at rest
|
- Sensitive data encrypted at rest
|
||||||
- HTTPS only in production
|
- HTTPS only in production
|
||||||
@@ -447,6 +473,7 @@ GET /api/automation/history - Automation execution history
|
|||||||
- XSS prevention (input sanitization, CSP headers)
|
- XSS prevention (input sanitization, CSP headers)
|
||||||
|
|
||||||
### File Upload Security
|
### File Upload Security
|
||||||
|
|
||||||
- File type validation (whitelist only)
|
- File type validation (whitelist only)
|
||||||
- File size limits (10MB per file)
|
- File size limits (10MB per file)
|
||||||
- Virus scanning (ClamAV integration for Phase 3)
|
- Virus scanning (ClamAV integration for Phase 3)
|
||||||
@@ -454,6 +481,7 @@ GET /api/automation/history - Automation execution history
|
|||||||
- Signed URLs with expiration
|
- Signed URLs with expiration
|
||||||
|
|
||||||
### API Security
|
### API Security
|
||||||
|
|
||||||
- Rate limiting (100 requests/min per user)
|
- Rate limiting (100 requests/min per user)
|
||||||
- Request size limits
|
- Request size limits
|
||||||
- API versioning (/api/v1/...)
|
- API versioning (/api/v1/...)
|
||||||
@@ -462,18 +490,21 @@ GET /api/automation/history - Automation execution history
|
|||||||
## Performance Considerations
|
## Performance Considerations
|
||||||
|
|
||||||
### Database
|
### Database
|
||||||
|
|
||||||
- Indexes on frequently queried fields
|
- Indexes on frequently queried fields
|
||||||
- Connection pooling
|
- Connection pooling
|
||||||
- Query optimization (EXPLAIN ANALYZE)
|
- Query optimization (EXPLAIN ANALYZE)
|
||||||
- Consider read replicas for Phase 3+
|
- Consider read replicas for Phase 3+
|
||||||
|
|
||||||
### Caching Strategy
|
### Caching Strategy
|
||||||
|
|
||||||
- Redis for session storage (Phase 2)
|
- Redis for session storage (Phase 2)
|
||||||
- Cache frequently accessed trees
|
- Cache frequently accessed trees
|
||||||
- CDN for static assets
|
- CDN for static assets
|
||||||
- Browser caching headers
|
- Browser caching headers
|
||||||
|
|
||||||
### Frontend Performance
|
### Frontend Performance
|
||||||
|
|
||||||
- Code splitting (lazy load routes)
|
- Code splitting (lazy load routes)
|
||||||
- Tree data cached in IndexedDB
|
- Tree data cached in IndexedDB
|
||||||
- Debounced search inputs
|
- Debounced search inputs
|
||||||
@@ -483,12 +514,14 @@ GET /api/automation/history - Automation execution history
|
|||||||
## Monitoring & Observability
|
## Monitoring & Observability
|
||||||
|
|
||||||
### Logging
|
### Logging
|
||||||
|
|
||||||
- Structured logging (JSON format)
|
- Structured logging (JSON format)
|
||||||
- Log levels: DEBUG, INFO, WARNING, ERROR, CRITICAL
|
- Log levels: DEBUG, INFO, WARNING, ERROR, CRITICAL
|
||||||
- Request ID tracking across services
|
- Request ID tracking across services
|
||||||
- User action auditing
|
- User action auditing
|
||||||
|
|
||||||
### Metrics (Phase 3)
|
### Metrics (Phase 3)
|
||||||
|
|
||||||
- API response times
|
- API response times
|
||||||
- Database query performance
|
- Database query performance
|
||||||
- Error rates
|
- Error rates
|
||||||
@@ -496,6 +529,7 @@ GET /api/automation/history - Automation execution history
|
|||||||
- System resource usage
|
- System resource usage
|
||||||
|
|
||||||
### Error Tracking
|
### Error Tracking
|
||||||
|
|
||||||
- Sentry integration for error tracking
|
- Sentry integration for error tracking
|
||||||
- User-friendly error messages
|
- User-friendly error messages
|
||||||
- Automatic error reporting with context
|
- Automatic error reporting with context
|
||||||
@@ -503,18 +537,21 @@ GET /api/automation/history - Automation execution history
|
|||||||
## Deployment Strategy
|
## Deployment Strategy
|
||||||
|
|
||||||
### CI/CD Pipeline
|
### CI/CD Pipeline
|
||||||
|
|
||||||
1. **Development:** Local development with hot reload
|
1. **Development:** Local development with hot reload
|
||||||
2. **Testing:** Automated tests on PR
|
2. **Testing:** Automated tests on PR
|
||||||
3. **Staging:** Auto-deploy to staging environment
|
3. **Staging:** Auto-deploy to staging environment
|
||||||
4. **Production:** Manual approval → deploy
|
4. **Production:** Manual approval → deploy
|
||||||
|
|
||||||
### Database Migrations
|
### Database Migrations
|
||||||
|
|
||||||
- Alembic for schema migrations
|
- Alembic for schema migrations
|
||||||
- Backwards-compatible changes
|
- Backwards-compatible changes
|
||||||
- Rollback capability
|
- Rollback capability
|
||||||
- Test migrations on staging first
|
- Test migrations on staging first
|
||||||
|
|
||||||
### Backup Strategy
|
### Backup Strategy
|
||||||
|
|
||||||
- Automated daily database backups
|
- Automated daily database backups
|
||||||
- Point-in-time recovery capability
|
- Point-in-time recovery capability
|
||||||
- File storage replication
|
- File storage replication
|
||||||
@@ -523,18 +560,21 @@ GET /api/automation/history - Automation execution history
|
|||||||
## Future Technical Considerations
|
## Future Technical Considerations
|
||||||
|
|
||||||
### Scalability
|
### Scalability
|
||||||
|
|
||||||
- Horizontal scaling (multiple app servers)
|
- Horizontal scaling (multiple app servers)
|
||||||
- Database sharding (by team_id)
|
- Database sharding (by team_id)
|
||||||
- Microservices architecture (if needed)
|
- Microservices architecture (if needed)
|
||||||
- Message queue for async tasks (Celery + Redis)
|
- Message queue for async tasks (Celery + Redis)
|
||||||
|
|
||||||
### Mobile Apps
|
### Mobile Apps
|
||||||
|
|
||||||
- React Native for iOS/Android
|
- React Native for iOS/Android
|
||||||
- Shared API backend
|
- Shared API backend
|
||||||
- Offline-first architecture
|
- Offline-first architecture
|
||||||
- Push notifications for team updates
|
- Push notifications for team updates
|
||||||
|
|
||||||
### AI/ML Integration (Phase 5+)
|
### AI/ML Integration (Phase 5+)
|
||||||
|
|
||||||
- Suggest next steps based on past sessions
|
- Suggest next steps based on past sessions
|
||||||
- Auto-categorize tickets
|
- Auto-categorize tickets
|
||||||
- Predict resolution time
|
- Predict resolution time
|
||||||
|
|||||||
148
backend/alembic/versions/7e00fa3c75c9_fix_datetime_timezone.py
Normal file
148
backend/alembic/versions/7e00fa3c75c9_fix_datetime_timezone.py
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
"""Fix datetime timezone
|
||||||
|
|
||||||
|
Revision ID: 7e00fa3c75c9
|
||||||
|
Revises: 001
|
||||||
|
Create Date: 2026-01-23 11:51:47.640123
|
||||||
|
|
||||||
|
"""
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy.dialects import postgresql
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision: str = '7e00fa3c75c9'
|
||||||
|
down_revision: Union[str, None] = '001'
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.alter_column('attachments', 'uploaded_at',
|
||||||
|
existing_type=postgresql.TIMESTAMP(),
|
||||||
|
nullable=False,
|
||||||
|
existing_server_default=sa.text('now()'))
|
||||||
|
op.alter_column('sessions', 'started_at',
|
||||||
|
existing_type=postgresql.TIMESTAMP(),
|
||||||
|
nullable=False,
|
||||||
|
existing_server_default=sa.text('now()'))
|
||||||
|
op.alter_column('sessions', 'exported',
|
||||||
|
existing_type=sa.BOOLEAN(),
|
||||||
|
nullable=False,
|
||||||
|
existing_server_default=sa.text('false'))
|
||||||
|
op.drop_index('idx_sessions_dates', table_name='sessions')
|
||||||
|
op.drop_index('idx_sessions_tree', table_name='sessions')
|
||||||
|
op.drop_index('idx_sessions_user', table_name='sessions')
|
||||||
|
op.create_index(op.f('ix_sessions_completed_at'), 'sessions', ['completed_at'], unique=False)
|
||||||
|
op.create_index(op.f('ix_sessions_started_at'), 'sessions', ['started_at'], unique=False)
|
||||||
|
op.create_index(op.f('ix_sessions_tree_id'), 'sessions', ['tree_id'], unique=False)
|
||||||
|
op.create_index(op.f('ix_sessions_user_id'), 'sessions', ['user_id'], unique=False)
|
||||||
|
op.alter_column('teams', 'created_at',
|
||||||
|
existing_type=postgresql.TIMESTAMP(),
|
||||||
|
nullable=False,
|
||||||
|
existing_server_default=sa.text('now()'))
|
||||||
|
op.alter_column('trees', 'is_active',
|
||||||
|
existing_type=sa.BOOLEAN(),
|
||||||
|
nullable=False,
|
||||||
|
existing_server_default=sa.text('true'))
|
||||||
|
op.alter_column('trees', 'version',
|
||||||
|
existing_type=sa.INTEGER(),
|
||||||
|
nullable=False,
|
||||||
|
existing_server_default=sa.text('1'))
|
||||||
|
op.alter_column('trees', 'created_at',
|
||||||
|
existing_type=postgresql.TIMESTAMP(),
|
||||||
|
nullable=False,
|
||||||
|
existing_server_default=sa.text('now()'))
|
||||||
|
op.alter_column('trees', 'updated_at',
|
||||||
|
existing_type=postgresql.TIMESTAMP(),
|
||||||
|
nullable=False,
|
||||||
|
existing_server_default=sa.text('now()'))
|
||||||
|
op.alter_column('trees', 'usage_count',
|
||||||
|
existing_type=sa.INTEGER(),
|
||||||
|
nullable=False,
|
||||||
|
existing_server_default=sa.text('0'))
|
||||||
|
op.drop_index('idx_trees_category', table_name='trees')
|
||||||
|
op.drop_index('idx_trees_search', table_name='trees', postgresql_using='gin')
|
||||||
|
op.drop_index('idx_trees_team', table_name='trees')
|
||||||
|
op.create_index(op.f('ix_trees_category'), 'trees', ['category'], unique=False)
|
||||||
|
op.create_index(op.f('ix_trees_team_id'), 'trees', ['team_id'], unique=False)
|
||||||
|
op.alter_column('users', 'created_at',
|
||||||
|
existing_type=postgresql.TIMESTAMP(),
|
||||||
|
type_=sa.DateTime(timezone=True),
|
||||||
|
nullable=False,
|
||||||
|
existing_server_default=sa.text('now()'))
|
||||||
|
op.alter_column('users', 'last_login',
|
||||||
|
existing_type=postgresql.TIMESTAMP(),
|
||||||
|
type_=sa.DateTime(timezone=True),
|
||||||
|
existing_nullable=True)
|
||||||
|
op.drop_index('idx_users_email', table_name='users')
|
||||||
|
op.drop_constraint('users_email_key', 'users', type_='unique')
|
||||||
|
op.create_index(op.f('ix_users_email'), 'users', ['email'], unique=True)
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_index(op.f('ix_users_email'), table_name='users')
|
||||||
|
op.create_unique_constraint('users_email_key', 'users', ['email'])
|
||||||
|
op.create_index('idx_users_email', 'users', ['email'], unique=True)
|
||||||
|
op.alter_column('users', 'last_login',
|
||||||
|
existing_type=sa.DateTime(timezone=True),
|
||||||
|
type_=postgresql.TIMESTAMP(),
|
||||||
|
existing_nullable=True)
|
||||||
|
op.alter_column('users', 'created_at',
|
||||||
|
existing_type=sa.DateTime(timezone=True),
|
||||||
|
type_=postgresql.TIMESTAMP(),
|
||||||
|
nullable=True,
|
||||||
|
existing_server_default=sa.text('now()'))
|
||||||
|
op.drop_index(op.f('ix_trees_team_id'), table_name='trees')
|
||||||
|
op.drop_index(op.f('ix_trees_category'), table_name='trees')
|
||||||
|
op.create_index('idx_trees_team', 'trees', ['team_id'], unique=False)
|
||||||
|
op.create_index('idx_trees_search', 'trees', [sa.text("to_tsvector('english'::regconfig, (COALESCE(name, ''::character varying)::text || ' '::text) || COALESCE(description, ''::text))")], unique=False, postgresql_using='gin')
|
||||||
|
op.create_index('idx_trees_category', 'trees', ['category'], unique=False)
|
||||||
|
op.alter_column('trees', 'usage_count',
|
||||||
|
existing_type=sa.INTEGER(),
|
||||||
|
nullable=True,
|
||||||
|
existing_server_default=sa.text('0'))
|
||||||
|
op.alter_column('trees', 'updated_at',
|
||||||
|
existing_type=postgresql.TIMESTAMP(),
|
||||||
|
nullable=True,
|
||||||
|
existing_server_default=sa.text('now()'))
|
||||||
|
op.alter_column('trees', 'created_at',
|
||||||
|
existing_type=postgresql.TIMESTAMP(),
|
||||||
|
nullable=True,
|
||||||
|
existing_server_default=sa.text('now()'))
|
||||||
|
op.alter_column('trees', 'version',
|
||||||
|
existing_type=sa.INTEGER(),
|
||||||
|
nullable=True,
|
||||||
|
existing_server_default=sa.text('1'))
|
||||||
|
op.alter_column('trees', 'is_active',
|
||||||
|
existing_type=sa.BOOLEAN(),
|
||||||
|
nullable=True,
|
||||||
|
existing_server_default=sa.text('true'))
|
||||||
|
op.alter_column('teams', 'created_at',
|
||||||
|
existing_type=postgresql.TIMESTAMP(),
|
||||||
|
nullable=True,
|
||||||
|
existing_server_default=sa.text('now()'))
|
||||||
|
op.drop_index(op.f('ix_sessions_user_id'), table_name='sessions')
|
||||||
|
op.drop_index(op.f('ix_sessions_tree_id'), table_name='sessions')
|
||||||
|
op.drop_index(op.f('ix_sessions_started_at'), table_name='sessions')
|
||||||
|
op.drop_index(op.f('ix_sessions_completed_at'), table_name='sessions')
|
||||||
|
op.create_index('idx_sessions_user', 'sessions', ['user_id'], unique=False)
|
||||||
|
op.create_index('idx_sessions_tree', 'sessions', ['tree_id'], unique=False)
|
||||||
|
op.create_index('idx_sessions_dates', 'sessions', ['started_at', 'completed_at'], unique=False)
|
||||||
|
op.alter_column('sessions', 'exported',
|
||||||
|
existing_type=sa.BOOLEAN(),
|
||||||
|
nullable=True,
|
||||||
|
existing_server_default=sa.text('false'))
|
||||||
|
op.alter_column('sessions', 'started_at',
|
||||||
|
existing_type=postgresql.TIMESTAMP(),
|
||||||
|
nullable=True,
|
||||||
|
existing_server_default=sa.text('now()'))
|
||||||
|
op.alter_column('attachments', 'uploaded_at',
|
||||||
|
existing_type=postgresql.TIMESTAMP(),
|
||||||
|
nullable=True,
|
||||||
|
existing_server_default=sa.text('now()'))
|
||||||
|
# ### end Alembic commands ###
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import uuid
|
import uuid
|
||||||
from datetime import datetime
|
from datetime import datetime, timezone
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from sqlalchemy import String, DateTime, ForeignKey
|
from sqlalchemy import String, DateTime, ForeignKey
|
||||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||||
@@ -18,17 +18,17 @@ class User(Base):
|
|||||||
email: Mapped[str] = mapped_column(String(255), unique=True, nullable=False, index=True)
|
email: Mapped[str] = mapped_column(String(255), unique=True, nullable=False, index=True)
|
||||||
password_hash: Mapped[str] = mapped_column(String(255), nullable=False)
|
password_hash: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||||
name: Mapped[str] = mapped_column(String(255), nullable=False)
|
name: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||||
role: Mapped[str] = mapped_column(String(50), nullable=False, default="engineer") # admin, engineer, viewer
|
role: Mapped[str] = mapped_column(String(50), nullable=False, default="engineer")
|
||||||
team_id: Mapped[Optional[uuid.UUID]] = mapped_column(
|
team_id: Mapped[Optional[uuid.UUID]] = mapped_column(
|
||||||
UUID(as_uuid=True),
|
UUID(as_uuid=True),
|
||||||
ForeignKey("teams.id"),
|
ForeignKey("teams.id"),
|
||||||
nullable=True
|
nullable=True
|
||||||
)
|
)
|
||||||
created_at: Mapped[datetime] = mapped_column(
|
created_at: Mapped[datetime] = mapped_column(
|
||||||
DateTime,
|
DateTime(timezone=True),
|
||||||
default=datetime.utcnow
|
default=lambda: datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
last_login: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
|
last_login: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||||
|
|
||||||
# Relationships
|
# Relationships
|
||||||
team: Mapped[Optional["Team"]] = relationship("Team", back_populates="users")
|
team: Mapped[Optional["Team"]] = relationship("Team", back_populates="users")
|
||||||
|
|||||||
30
backend/requirements-windows.txt
Normal file
30
backend/requirements-windows.txt
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
# FastAPI and dependencies
|
||||||
|
fastapi==0.109.2
|
||||||
|
uvicorn[standard]==0.27.1
|
||||||
|
python-multipart==0.0.9
|
||||||
|
|
||||||
|
# Pydantic with pre-built wheels
|
||||||
|
pydantic==2.6.1
|
||||||
|
pydantic-settings==2.1.0
|
||||||
|
pydantic-core==2.16.2
|
||||||
|
annotated-types==0.6.0
|
||||||
|
|
||||||
|
# Database
|
||||||
|
sqlalchemy[asyncio]==2.0.27
|
||||||
|
asyncpg==0.29.0
|
||||||
|
alembic==1.13.1
|
||||||
|
|
||||||
|
# Authentication
|
||||||
|
python-jose[cryptography]==3.3.0
|
||||||
|
passlib[bcrypt]==1.7.4
|
||||||
|
bcrypt==4.1.2
|
||||||
|
|
||||||
|
# Security
|
||||||
|
cryptography==42.0.2
|
||||||
|
|
||||||
|
# Email validation
|
||||||
|
email-validator==2.1.0.post1
|
||||||
|
|
||||||
|
# Others
|
||||||
|
starlette==0.36.3
|
||||||
|
typing-extensions==4.9.0
|
||||||
Reference in New Issue
Block a user