mirror of
https://github.com/blackboxprogramming/BlackRoad-Operating-System.git
synced 2026-03-17 02:57:12 -05:00
Add comprehensive FastAPI backend for BlackRoad OS
This commit adds a complete backend infrastructure with: **Core Infrastructure:** - FastAPI application with async/await support - PostgreSQL database with SQLAlchemy ORM - Redis caching layer - JWT authentication and authorization - Docker and Docker Compose configuration **API Services:** - Authentication API (register, login, JWT tokens) - RoadMail API (email service with folders, send/receive) - BlackRoad Social API (posts, comments, likes, follows) - BlackStream API (video streaming with views/likes) - File Storage API (file explorer with upload/download) - RoadCoin Blockchain API (mining, transactions, wallet) - AI Chat API (conversations with AI assistant) **Database Models:** - User accounts with wallet integration - Email and folder management - Social media posts and engagement - Video metadata and analytics - File storage with sharing - Blockchain blocks and transactions - AI conversation history **Features:** - Complete CRUD operations for all services - Real-time blockchain mining with proof-of-work - Transaction validation and wallet management - File upload with S3 integration (ready) - Social feed with engagement metrics - Email system with threading support - AI chat with conversation persistence **Documentation:** - Comprehensive README with setup instructions - API documentation (Swagger/ReDoc auto-generated) - Deployment guide for multiple platforms - Testing framework with pytest **DevOps:** - Docker containerization - Docker Compose for local development - Database migrations with Alembic - Health check endpoints - Makefile for common tasks All APIs are production-ready with proper error handling, input validation, and security measures.
This commit is contained in:
28
backend/app/models/__init__.py
Normal file
28
backend/app/models/__init__.py
Normal file
@@ -0,0 +1,28 @@
|
||||
"""Database models"""
|
||||
from app.models.user import User
|
||||
from app.models.email import Email, EmailFolder
|
||||
from app.models.social import Post, Comment, Like, Follow
|
||||
from app.models.video import Video, VideoView, VideoLike
|
||||
from app.models.file import File, Folder
|
||||
from app.models.blockchain import Block, Transaction, Wallet
|
||||
from app.models.ai_chat import Conversation, Message
|
||||
|
||||
__all__ = [
|
||||
"User",
|
||||
"Email",
|
||||
"EmailFolder",
|
||||
"Post",
|
||||
"Comment",
|
||||
"Like",
|
||||
"Follow",
|
||||
"Video",
|
||||
"VideoView",
|
||||
"VideoLike",
|
||||
"File",
|
||||
"Folder",
|
||||
"Block",
|
||||
"Transaction",
|
||||
"Wallet",
|
||||
"Conversation",
|
||||
"Message",
|
||||
]
|
||||
56
backend/app/models/ai_chat.py
Normal file
56
backend/app/models/ai_chat.py
Normal file
@@ -0,0 +1,56 @@
|
||||
"""AI Chat models"""
|
||||
from sqlalchemy import Column, Integer, String, Text, DateTime, ForeignKey, Enum
|
||||
from sqlalchemy.sql import func
|
||||
import enum
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class MessageRole(str, enum.Enum):
|
||||
"""Message role types"""
|
||||
USER = "user"
|
||||
ASSISTANT = "assistant"
|
||||
SYSTEM = "system"
|
||||
|
||||
|
||||
class Conversation(Base):
|
||||
"""AI conversation model"""
|
||||
|
||||
__tablename__ = "conversations"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False)
|
||||
|
||||
title = Column(String(255))
|
||||
model = Column(String(100), default="gpt-3.5-turbo")
|
||||
|
||||
# Metadata
|
||||
message_count = Column(Integer, default=0)
|
||||
total_tokens = Column(Integer, default=0)
|
||||
|
||||
# Timestamps
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
||||
|
||||
|
||||
class Message(Base):
|
||||
"""AI chat message model"""
|
||||
|
||||
__tablename__ = "messages"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
conversation_id = Column(Integer, ForeignKey("conversations.id", ondelete="CASCADE"), nullable=False)
|
||||
|
||||
role = Column(Enum(MessageRole), nullable=False)
|
||||
content = Column(Text, nullable=False)
|
||||
|
||||
# Token usage
|
||||
tokens = Column(Integer)
|
||||
|
||||
# Metadata
|
||||
model = Column(String(100))
|
||||
finish_reason = Column(String(50))
|
||||
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
def __repr__(self):
|
||||
return f"<Message {self.id} ({self.role})>"
|
||||
94
backend/app/models/blockchain.py
Normal file
94
backend/app/models/blockchain.py
Normal file
@@ -0,0 +1,94 @@
|
||||
"""Blockchain and cryptocurrency models"""
|
||||
from sqlalchemy import Column, Integer, String, Text, DateTime, ForeignKey, Float, Boolean
|
||||
from sqlalchemy.sql import func
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class Wallet(Base):
|
||||
"""Cryptocurrency wallet model"""
|
||||
|
||||
__tablename__ = "wallets"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False)
|
||||
|
||||
address = Column(String(255), unique=True, nullable=False, index=True)
|
||||
private_key = Column(String(500), nullable=False) # Encrypted
|
||||
public_key = Column(String(500), nullable=False)
|
||||
|
||||
balance = Column(Float, default=0.0)
|
||||
|
||||
# Metadata
|
||||
label = Column(String(100))
|
||||
is_primary = Column(Boolean, default=True)
|
||||
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
||||
|
||||
|
||||
class Transaction(Base):
|
||||
"""Blockchain transaction model"""
|
||||
|
||||
__tablename__ = "transactions"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
|
||||
# Transaction details
|
||||
transaction_hash = Column(String(255), unique=True, nullable=False, index=True)
|
||||
from_address = Column(String(255), nullable=False, index=True)
|
||||
to_address = Column(String(255), nullable=False, index=True)
|
||||
amount = Column(Float, nullable=False)
|
||||
fee = Column(Float, default=0.0)
|
||||
|
||||
# Block information
|
||||
block_id = Column(Integer, ForeignKey("blocks.id", ondelete="SET NULL"))
|
||||
block_index = Column(Integer)
|
||||
|
||||
# Status
|
||||
is_confirmed = Column(Boolean, default=False)
|
||||
confirmations = Column(Integer, default=0)
|
||||
|
||||
# Metadata
|
||||
signature = Column(Text, nullable=False)
|
||||
message = Column(Text) # Optional transaction message
|
||||
|
||||
# Timestamps
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
confirmed_at = Column(DateTime(timezone=True))
|
||||
|
||||
def __repr__(self):
|
||||
return f"<Transaction {self.transaction_hash}>"
|
||||
|
||||
|
||||
class Block(Base):
|
||||
"""Blockchain block model"""
|
||||
|
||||
__tablename__ = "blocks"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
|
||||
# Block data
|
||||
index = Column(Integer, unique=True, nullable=False, index=True)
|
||||
timestamp = Column(DateTime(timezone=True), nullable=False)
|
||||
nonce = Column(Integer, nullable=False)
|
||||
previous_hash = Column(String(255), nullable=False)
|
||||
hash = Column(String(255), unique=True, nullable=False, index=True)
|
||||
merkle_root = Column(String(255))
|
||||
|
||||
# Mining
|
||||
miner_id = Column(Integer, ForeignKey("users.id", ondelete="SET NULL"))
|
||||
miner_address = Column(String(255))
|
||||
difficulty = Column(Integer, nullable=False)
|
||||
reward = Column(Float, default=0.0)
|
||||
|
||||
# Block metadata
|
||||
transaction_count = Column(Integer, default=0)
|
||||
size = Column(Integer) # in bytes
|
||||
|
||||
# Validation
|
||||
is_valid = Column(Boolean, default=True)
|
||||
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
def __repr__(self):
|
||||
return f"<Block {self.index}>"
|
||||
76
backend/app/models/email.py
Normal file
76
backend/app/models/email.py
Normal file
@@ -0,0 +1,76 @@
|
||||
"""Email models"""
|
||||
from sqlalchemy import Column, Integer, String, Text, Boolean, DateTime, ForeignKey, Enum
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy.sql import func
|
||||
import enum
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class EmailFolderType(str, enum.Enum):
|
||||
"""Email folder types"""
|
||||
INBOX = "inbox"
|
||||
SENT = "sent"
|
||||
DRAFTS = "drafts"
|
||||
SPAM = "spam"
|
||||
TRASH = "trash"
|
||||
CUSTOM = "custom"
|
||||
|
||||
|
||||
class EmailFolder(Base):
|
||||
"""Email folder model"""
|
||||
|
||||
__tablename__ = "email_folders"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False)
|
||||
name = Column(String(100), nullable=False)
|
||||
folder_type = Column(Enum(EmailFolderType), default=EmailFolderType.CUSTOM)
|
||||
icon = Column(String(50))
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
|
||||
class Email(Base):
|
||||
"""Email model"""
|
||||
|
||||
__tablename__ = "emails"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
|
||||
# Sender/Receiver
|
||||
sender_id = Column(Integer, ForeignKey("users.id", ondelete="SET NULL"))
|
||||
sender_email = Column(String(255), nullable=False)
|
||||
sender_name = Column(String(255))
|
||||
|
||||
recipient_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False)
|
||||
recipient_email = Column(String(255), nullable=False)
|
||||
|
||||
# CC/BCC
|
||||
cc = Column(Text) # Comma-separated emails
|
||||
bcc = Column(Text) # Comma-separated emails
|
||||
|
||||
# Email content
|
||||
subject = Column(String(500), nullable=False)
|
||||
body = Column(Text, nullable=False)
|
||||
html_body = Column(Text)
|
||||
|
||||
# Metadata
|
||||
folder_id = Column(Integer, ForeignKey("email_folders.id", ondelete="SET NULL"))
|
||||
is_read = Column(Boolean, default=False)
|
||||
is_starred = Column(Boolean, default=False)
|
||||
is_draft = Column(Boolean, default=False)
|
||||
is_spam = Column(Boolean, default=False)
|
||||
|
||||
# Attachments (stored as JSON array of file IDs)
|
||||
attachment_ids = Column(Text)
|
||||
|
||||
# Thread
|
||||
thread_id = Column(String(255), index=True)
|
||||
in_reply_to = Column(Integer, ForeignKey("emails.id", ondelete="SET NULL"))
|
||||
|
||||
# Timestamps
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
sent_at = Column(DateTime(timezone=True))
|
||||
read_at = Column(DateTime(timezone=True))
|
||||
|
||||
def __repr__(self):
|
||||
return f"<Email {self.id}: {self.subject}>"
|
||||
64
backend/app/models/file.py
Normal file
64
backend/app/models/file.py
Normal file
@@ -0,0 +1,64 @@
|
||||
"""File system models"""
|
||||
from sqlalchemy import Column, Integer, String, Text, Boolean, DateTime, ForeignKey, BigInteger
|
||||
from sqlalchemy.sql import func
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class Folder(Base):
|
||||
"""Folder model"""
|
||||
|
||||
__tablename__ = "folders"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False)
|
||||
|
||||
name = Column(String(255), nullable=False)
|
||||
parent_id = Column(Integer, ForeignKey("folders.id", ondelete="CASCADE"))
|
||||
path = Column(String(1000), nullable=False) # Full path for quick lookups
|
||||
|
||||
is_shared = Column(Boolean, default=False)
|
||||
is_public = Column(Boolean, default=False)
|
||||
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
||||
|
||||
|
||||
class File(Base):
|
||||
"""File model"""
|
||||
|
||||
__tablename__ = "files"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False)
|
||||
folder_id = Column(Integer, ForeignKey("folders.id", ondelete="CASCADE"))
|
||||
|
||||
name = Column(String(255), nullable=False)
|
||||
original_name = Column(String(255), nullable=False)
|
||||
path = Column(String(1000), nullable=False)
|
||||
|
||||
# File metadata
|
||||
file_type = Column(String(100)) # MIME type
|
||||
extension = Column(String(20))
|
||||
size = Column(BigInteger, nullable=False) # in bytes
|
||||
|
||||
# Storage
|
||||
storage_key = Column(String(500), nullable=False) # S3 key or local path
|
||||
storage_url = Column(String(1000)) # Public URL if available
|
||||
checksum = Column(String(64)) # SHA-256 hash
|
||||
|
||||
# Sharing
|
||||
is_shared = Column(Boolean, default=False)
|
||||
is_public = Column(Boolean, default=False)
|
||||
share_token = Column(String(255), unique=True)
|
||||
|
||||
# Metadata
|
||||
description = Column(Text)
|
||||
tags = Column(Text) # Comma-separated
|
||||
|
||||
# Timestamps
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
||||
last_accessed = Column(DateTime(timezone=True))
|
||||
|
||||
def __repr__(self):
|
||||
return f"<File {self.name}>"
|
||||
75
backend/app/models/social.py
Normal file
75
backend/app/models/social.py
Normal file
@@ -0,0 +1,75 @@
|
||||
"""Social media models"""
|
||||
from sqlalchemy import Column, Integer, String, Text, Boolean, DateTime, ForeignKey
|
||||
from sqlalchemy.sql import func
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class Post(Base):
|
||||
"""Social media post model"""
|
||||
|
||||
__tablename__ = "posts"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False)
|
||||
content = Column(Text, nullable=False)
|
||||
image_url = Column(String(500))
|
||||
video_url = Column(String(500))
|
||||
|
||||
# Engagement metrics
|
||||
likes_count = Column(Integer, default=0)
|
||||
comments_count = Column(Integer, default=0)
|
||||
shares_count = Column(Integer, default=0)
|
||||
views_count = Column(Integer, default=0)
|
||||
|
||||
# Visibility
|
||||
is_public = Column(Boolean, default=True)
|
||||
is_pinned = Column(Boolean, default=False)
|
||||
|
||||
# Timestamps
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
||||
|
||||
def __repr__(self):
|
||||
return f"<Post {self.id}>"
|
||||
|
||||
|
||||
class Comment(Base):
|
||||
"""Comment model"""
|
||||
|
||||
__tablename__ = "comments"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
post_id = Column(Integer, ForeignKey("posts.id", ondelete="CASCADE"), nullable=False)
|
||||
user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False)
|
||||
content = Column(Text, nullable=False)
|
||||
parent_id = Column(Integer, ForeignKey("comments.id", ondelete="CASCADE")) # For nested comments
|
||||
|
||||
likes_count = Column(Integer, default=0)
|
||||
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
||||
|
||||
|
||||
class Like(Base):
|
||||
"""Like model"""
|
||||
|
||||
__tablename__ = "likes"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False)
|
||||
post_id = Column(Integer, ForeignKey("posts.id", ondelete="CASCADE"))
|
||||
comment_id = Column(Integer, ForeignKey("comments.id", ondelete="CASCADE"))
|
||||
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
|
||||
class Follow(Base):
|
||||
"""Follow relationship model"""
|
||||
|
||||
__tablename__ = "follows"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
follower_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False)
|
||||
following_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False)
|
||||
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
34
backend/app/models/user.py
Normal file
34
backend/app/models/user.py
Normal file
@@ -0,0 +1,34 @@
|
||||
"""User model"""
|
||||
from sqlalchemy import Column, Integer, String, Boolean, DateTime, Float
|
||||
from sqlalchemy.sql import func
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class User(Base):
|
||||
"""User model"""
|
||||
|
||||
__tablename__ = "users"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
username = Column(String(50), unique=True, index=True, nullable=False)
|
||||
email = Column(String(255), unique=True, index=True, nullable=False)
|
||||
hashed_password = Column(String(255), nullable=False)
|
||||
full_name = Column(String(255))
|
||||
avatar_url = Column(String(500))
|
||||
bio = Column(String(500))
|
||||
is_active = Column(Boolean, default=True)
|
||||
is_verified = Column(Boolean, default=False)
|
||||
is_admin = Column(Boolean, default=False)
|
||||
|
||||
# Blockchain wallet
|
||||
wallet_address = Column(String(255), unique=True, index=True)
|
||||
wallet_private_key = Column(String(500)) # Encrypted
|
||||
balance = Column(Float, default=0.0)
|
||||
|
||||
# Timestamps
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
||||
last_login = Column(DateTime(timezone=True))
|
||||
|
||||
def __repr__(self):
|
||||
return f"<User {self.username}>"
|
||||
78
backend/app/models/video.py
Normal file
78
backend/app/models/video.py
Normal file
@@ -0,0 +1,78 @@
|
||||
"""Video streaming models"""
|
||||
from sqlalchemy import Column, Integer, String, Text, Boolean, DateTime, ForeignKey, Float
|
||||
from sqlalchemy.sql import func
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class Video(Base):
|
||||
"""Video model"""
|
||||
|
||||
__tablename__ = "videos"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False)
|
||||
|
||||
title = Column(String(255), nullable=False)
|
||||
description = Column(Text)
|
||||
thumbnail_url = Column(String(500))
|
||||
video_url = Column(String(500), nullable=False)
|
||||
|
||||
# Video metadata
|
||||
duration = Column(Integer) # in seconds
|
||||
resolution = Column(String(20)) # e.g., "1920x1080"
|
||||
file_size = Column(Integer) # in bytes
|
||||
format = Column(String(20)) # e.g., "mp4", "webm"
|
||||
|
||||
# Engagement
|
||||
views_count = Column(Integer, default=0)
|
||||
likes_count = Column(Integer, default=0)
|
||||
dislikes_count = Column(Integer, default=0)
|
||||
comments_count = Column(Integer, default=0)
|
||||
|
||||
# Visibility
|
||||
is_public = Column(Boolean, default=True)
|
||||
is_live = Column(Boolean, default=False)
|
||||
is_processing = Column(Boolean, default=False)
|
||||
|
||||
# Categories/Tags
|
||||
category = Column(String(100))
|
||||
tags = Column(Text) # Comma-separated
|
||||
|
||||
# Timestamps
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
||||
published_at = Column(DateTime(timezone=True))
|
||||
|
||||
|
||||
class VideoView(Base):
|
||||
"""Video view tracking"""
|
||||
|
||||
__tablename__ = "video_views"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
video_id = Column(Integer, ForeignKey("videos.id", ondelete="CASCADE"), nullable=False)
|
||||
user_id = Column(Integer, ForeignKey("users.id", ondelete="SET NULL"))
|
||||
|
||||
# Watch metadata
|
||||
watch_duration = Column(Integer) # seconds watched
|
||||
completion_percentage = Column(Float)
|
||||
|
||||
# Analytics
|
||||
ip_address = Column(String(45))
|
||||
user_agent = Column(String(500))
|
||||
referrer = Column(String(500))
|
||||
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
|
||||
class VideoLike(Base):
|
||||
"""Video like/dislike tracking"""
|
||||
|
||||
__tablename__ = "video_likes"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
video_id = Column(Integer, ForeignKey("videos.id", ondelete="CASCADE"), nullable=False)
|
||||
user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False)
|
||||
is_like = Column(Boolean, default=True) # True=like, False=dislike
|
||||
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
Reference in New Issue
Block a user