Fix backend: add passlib/bcrypt, fix datetime timezone issues
This commit is contained in:
@@ -1,11 +1,15 @@
|
||||
# Troubleshooting Decision Tree Application
|
||||
|
||||
## Project Overview
|
||||
|
||||
### Vision
|
||||
|
||||
A decision tree troubleshooting application designed for MSP engineers to transform diagnostic processes into clean, professional documentation automatically.
|
||||
|
||||
### 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:
|
||||
|
||||
- Takes 15-25 minutes to regain full focus
|
||||
- Creates cognitive overhead and attention residue
|
||||
- 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
|
||||
|
||||
### Solution
|
||||
|
||||
An intelligent decision tree system that:
|
||||
|
||||
- Guides engineers through proven troubleshooting paths
|
||||
- Captures decisions and notes automatically
|
||||
- Generates professional ticket documentation
|
||||
@@ -21,9 +27,11 @@ An intelligent decision tree system that:
|
||||
- Reduces cognitive load during high-stress situations
|
||||
|
||||
### Success Criteria
|
||||
|
||||
**3-Month Goal:** Michael uses this tool for 50% of his tickets
|
||||
|
||||
**Key Metrics:**
|
||||
|
||||
- Time saved per ticket
|
||||
- Documentation quality improvement
|
||||
- Reduction in "what did I do?" moments
|
||||
@@ -31,14 +39,17 @@ An intelligent decision tree system that:
|
||||
- Reduction in repeated troubleshooting attempts
|
||||
|
||||
### Target Users
|
||||
|
||||
**Primary:** Senior Systems Engineers at MSPs managing Windows Server, Active Directory, Citrix, networking equipment
|
||||
|
||||
**Secondary:**
|
||||
**Secondary:**
|
||||
|
||||
- Junior engineers needing guided troubleshooting
|
||||
- Onsite technicians following remote engineer instructions
|
||||
- MSP teams wanting standardized procedures
|
||||
|
||||
### Key Differentiators
|
||||
|
||||
1. **Automatic documentation generation** - No separate note-taking step
|
||||
2. **On-the-fly customization** - Add custom branches when encountering edge cases
|
||||
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
|
||||
|
||||
### Potential Market
|
||||
|
||||
- 30,000+ MSPs in North America alone
|
||||
- Average MSP has 15-50 technical staff
|
||||
- Adjacent markets: Internal IT teams, DevOps, Technical Support
|
||||
|
||||
### Monetization Possibilities
|
||||
|
||||
- **Free Tier:** Personal use, limited trees
|
||||
- **Pro Tier:** Team sharing, unlimited trees, analytics
|
||||
- **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.
|
||||
|
||||
### Name Ideas (To Workshop)
|
||||
|
||||
- TroubleTree
|
||||
- DecisionPath
|
||||
- MSP Navigator
|
||||
@@ -71,7 +85,9 @@ An intelligent decision tree system that:
|
||||
- DiagPath
|
||||
|
||||
### Competitive Landscape
|
||||
|
||||
**Current Solutions:**
|
||||
|
||||
- Static runbooks/wiki pages (not interactive)
|
||||
- Flowchart tools (not designed for real-time troubleshooting)
|
||||
- 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.
|
||||
|
||||
### Technology Philosophy
|
||||
|
||||
- **Web-first:** Accessible anywhere, no installation
|
||||
- **Progressive enhancement:** Works offline, syncs when online
|
||||
- **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
|
||||
|
||||
### Project Status
|
||||
|
||||
**Current Phase:** Planning and Architecture
|
||||
**Next Phase:** MVP Development (Weeks 1-3)
|
||||
**Target MVP Date:** 3 weeks from project start
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
## System Architecture
|
||||
|
||||
### High-Level Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ Frontend (React/Vue) │
|
||||
@@ -41,7 +42,9 @@
|
||||
## Tech Stack
|
||||
|
||||
### Frontend
|
||||
|
||||
**Primary Choice: React**
|
||||
|
||||
- **Pros:** Large ecosystem, excellent offline support (PWA), familiar to most developers
|
||||
- **Alternatives:** Vue.js (simpler), Svelte (faster)
|
||||
- **UI Framework:** Tailwind CSS + shadcn/ui (clean, professional look)
|
||||
@@ -50,7 +53,9 @@
|
||||
- **Offline:** Service Workers + IndexedDB for offline tree caching
|
||||
|
||||
### Backend
|
||||
|
||||
**Primary Choice: Python FastAPI**
|
||||
|
||||
- **Pros:** Modern, fast, async support, automatic API docs, matches Michael's learning path
|
||||
- **Alternatives:** Flask (simpler but less performant), Django (heavier)
|
||||
- **Authentication:** JWT tokens + httpOnly cookies
|
||||
@@ -59,26 +64,33 @@
|
||||
- **Migration:** Alembic
|
||||
|
||||
### Database
|
||||
|
||||
**Primary Choice: PostgreSQL**
|
||||
|
||||
- **Pros:** JSON/JSONB support perfect for tree storage, reliable, scalable
|
||||
- **Schema Design:**
|
||||
- **Schema Design:**
|
||||
- Hybrid approach: Relational for users/sessions, JSONB for tree structure
|
||||
- Full-text search for tree discovery
|
||||
- Indexes on frequently queried fields
|
||||
|
||||
### File Storage
|
||||
|
||||
**Primary Choice: S3-compatible storage**
|
||||
|
||||
- **Development:** MinIO (self-hosted, S3-compatible)
|
||||
- **Production:** AWS S3 or DigitalOcean Spaces
|
||||
- **Strategy:** Pre-signed URLs for uploads, CDN for delivery
|
||||
|
||||
### Hosting
|
||||
|
||||
**Development:**
|
||||
|
||||
- Frontend: Local dev server (Vite)
|
||||
- Backend: Local Python server
|
||||
- Database: Docker PostgreSQL
|
||||
|
||||
**Production Options:**
|
||||
|
||||
1. **Simple Start:** Railway or Render (full-stack hosting)
|
||||
- Cost: ~$10-20/month
|
||||
- Pros: Easy deployment, managed databases
|
||||
@@ -99,6 +111,7 @@
|
||||
### Database Schema
|
||||
|
||||
#### Users Table
|
||||
|
||||
```sql
|
||||
CREATE TABLE users (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
@@ -113,6 +126,7 @@ CREATE TABLE users (
|
||||
```
|
||||
|
||||
#### Teams Table
|
||||
|
||||
```sql
|
||||
CREATE TABLE teams (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
@@ -122,6 +136,7 @@ CREATE TABLE teams (
|
||||
```
|
||||
|
||||
#### Trees Table
|
||||
|
||||
```sql
|
||||
CREATE TABLE trees (
|
||||
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
|
||||
|
||||
```sql
|
||||
CREATE TABLE sessions (
|
||||
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
|
||||
|
||||
```sql
|
||||
CREATE TABLE attachments (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
@@ -368,6 +385,7 @@ CREATE TABLE attachments (
|
||||
## API Endpoints
|
||||
|
||||
### Authentication
|
||||
|
||||
```
|
||||
POST /api/auth/register - Register new user
|
||||
POST /api/auth/login - Login
|
||||
@@ -377,6 +395,7 @@ POST /api/auth/refresh - Refresh JWT token
|
||||
```
|
||||
|
||||
### Trees
|
||||
|
||||
```
|
||||
GET /api/trees - List all trees (with filters)
|
||||
GET /api/trees/:id - Get specific tree
|
||||
@@ -388,6 +407,7 @@ GET /api/trees/search - Full-text search trees
|
||||
```
|
||||
|
||||
### Sessions
|
||||
|
||||
```
|
||||
GET /api/sessions - List user's sessions
|
||||
GET /api/sessions/:id - Get specific session
|
||||
@@ -398,6 +418,7 @@ POST /api/sessions/:id/export - Export session to formatted notes
|
||||
```
|
||||
|
||||
### Attachments
|
||||
|
||||
```
|
||||
POST /api/sessions/:id/attachments - Upload attachment
|
||||
GET /api/sessions/:id/attachments - List attachments
|
||||
@@ -406,6 +427,7 @@ DELETE /api/attachments/:id - Delete attachment
|
||||
```
|
||||
|
||||
### Teams (Phase 2)
|
||||
|
||||
```
|
||||
GET /api/teams - List teams
|
||||
POST /api/teams - Create team (admin only)
|
||||
@@ -415,6 +437,7 @@ DELETE /api/teams/:id/members/:user_id - Remove team member
|
||||
```
|
||||
|
||||
### Analytics (Phase 3)
|
||||
|
||||
```
|
||||
GET /api/analytics/trees/:id/usage - Tree usage statistics
|
||||
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)
|
||||
|
||||
```
|
||||
GET /api/automation/scripts - List available automation scripts
|
||||
POST /api/automation/execute - Execute automation script
|
||||
@@ -432,6 +456,7 @@ GET /api/automation/history - Automation execution history
|
||||
## Security Considerations
|
||||
|
||||
### Authentication & Authorization
|
||||
|
||||
- JWT tokens with short expiry (15 min access, 7 day refresh)
|
||||
- Role-based access control (RBAC)
|
||||
- Password requirements: min 10 chars, complexity
|
||||
@@ -439,6 +464,7 @@ GET /api/automation/history - Automation execution history
|
||||
- Account lockout after failed attempts
|
||||
|
||||
### Data Protection
|
||||
|
||||
- All passwords hashed with bcrypt (cost factor 12)
|
||||
- Sensitive data encrypted at rest
|
||||
- HTTPS only in production
|
||||
@@ -447,6 +473,7 @@ GET /api/automation/history - Automation execution history
|
||||
- XSS prevention (input sanitization, CSP headers)
|
||||
|
||||
### File Upload Security
|
||||
|
||||
- File type validation (whitelist only)
|
||||
- File size limits (10MB per file)
|
||||
- Virus scanning (ClamAV integration for Phase 3)
|
||||
@@ -454,6 +481,7 @@ GET /api/automation/history - Automation execution history
|
||||
- Signed URLs with expiration
|
||||
|
||||
### API Security
|
||||
|
||||
- Rate limiting (100 requests/min per user)
|
||||
- Request size limits
|
||||
- API versioning (/api/v1/...)
|
||||
@@ -462,18 +490,21 @@ GET /api/automation/history - Automation execution history
|
||||
## Performance Considerations
|
||||
|
||||
### Database
|
||||
|
||||
- Indexes on frequently queried fields
|
||||
- Connection pooling
|
||||
- Query optimization (EXPLAIN ANALYZE)
|
||||
- Consider read replicas for Phase 3+
|
||||
|
||||
### Caching Strategy
|
||||
|
||||
- Redis for session storage (Phase 2)
|
||||
- Cache frequently accessed trees
|
||||
- CDN for static assets
|
||||
- Browser caching headers
|
||||
|
||||
### Frontend Performance
|
||||
|
||||
- Code splitting (lazy load routes)
|
||||
- Tree data cached in IndexedDB
|
||||
- Debounced search inputs
|
||||
@@ -483,12 +514,14 @@ GET /api/automation/history - Automation execution history
|
||||
## Monitoring & Observability
|
||||
|
||||
### Logging
|
||||
|
||||
- Structured logging (JSON format)
|
||||
- Log levels: DEBUG, INFO, WARNING, ERROR, CRITICAL
|
||||
- Request ID tracking across services
|
||||
- User action auditing
|
||||
|
||||
### Metrics (Phase 3)
|
||||
|
||||
- API response times
|
||||
- Database query performance
|
||||
- Error rates
|
||||
@@ -496,6 +529,7 @@ GET /api/automation/history - Automation execution history
|
||||
- System resource usage
|
||||
|
||||
### Error Tracking
|
||||
|
||||
- Sentry integration for error tracking
|
||||
- User-friendly error messages
|
||||
- Automatic error reporting with context
|
||||
@@ -503,18 +537,21 @@ GET /api/automation/history - Automation execution history
|
||||
## Deployment Strategy
|
||||
|
||||
### CI/CD Pipeline
|
||||
|
||||
1. **Development:** Local development with hot reload
|
||||
2. **Testing:** Automated tests on PR
|
||||
3. **Staging:** Auto-deploy to staging environment
|
||||
4. **Production:** Manual approval → deploy
|
||||
|
||||
### Database Migrations
|
||||
|
||||
- Alembic for schema migrations
|
||||
- Backwards-compatible changes
|
||||
- Rollback capability
|
||||
- Test migrations on staging first
|
||||
|
||||
### Backup Strategy
|
||||
|
||||
- Automated daily database backups
|
||||
- Point-in-time recovery capability
|
||||
- File storage replication
|
||||
@@ -523,18 +560,21 @@ GET /api/automation/history - Automation execution history
|
||||
## Future Technical Considerations
|
||||
|
||||
### Scalability
|
||||
|
||||
- Horizontal scaling (multiple app servers)
|
||||
- Database sharding (by team_id)
|
||||
- Microservices architecture (if needed)
|
||||
- Message queue for async tasks (Celery + Redis)
|
||||
|
||||
### Mobile Apps
|
||||
|
||||
- React Native for iOS/Android
|
||||
- Shared API backend
|
||||
- Offline-first architecture
|
||||
- Push notifications for team updates
|
||||
|
||||
### AI/ML Integration (Phase 5+)
|
||||
|
||||
- Suggest next steps based on past sessions
|
||||
- Auto-categorize tickets
|
||||
- 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
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timezone
|
||||
from typing import Optional
|
||||
from sqlalchemy import String, DateTime, ForeignKey
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
@@ -18,19 +18,19 @@ class User(Base):
|
||||
email: Mapped[str] = mapped_column(String(255), unique=True, nullable=False, index=True)
|
||||
password_hash: 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(
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("teams.id"),
|
||||
nullable=True
|
||||
)
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime,
|
||||
default=datetime.utcnow
|
||||
DateTime(timezone=True),
|
||||
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
|
||||
team: Mapped[Optional["Team"]] = relationship("Team", back_populates="users")
|
||||
trees: Mapped[list["Tree"]] = relationship("Tree", back_populates="author")
|
||||
sessions: Mapped[list["Session"]] = relationship("Session", back_populates="user")
|
||||
sessions: Mapped[list["Session"]] = relationship("Session", back_populates="user")
|
||||
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