
- Built complete CEX platform with FastAPI and Python - JWT-based authentication system with secure password hashing - Multi-currency crypto wallet support (BTC, ETH, USDT) - Fiat account management (USD, EUR, GBP) - Local transaction signing without external APIs - Comprehensive transaction handling (send/receive/deposit/withdraw) - SQLAlchemy models with Alembic migrations - Security middleware (rate limiting, headers, logging) - Input validation and sanitization - Encrypted private key storage with PBKDF2 - Standardized codebase architecture with service layer pattern - Complete API documentation with health endpoints - Comprehensive README with setup instructions Features: - User registration and authentication - Crypto wallet creation and management - Secure transaction signing using local private keys - Fiat deposit/withdrawal system - Transaction history and tracking - Rate limiting and security headers - Input validation for all endpoints - Error handling and logging
127 lines
6.5 KiB
Python
127 lines
6.5 KiB
Python
"""Initial migration
|
|
|
|
Revision ID: 001
|
|
Revises:
|
|
Create Date: 2025-06-20 12:00:00.000000
|
|
|
|
"""
|
|
from alembic import op
|
|
import sqlalchemy as sa
|
|
|
|
# revision identifiers, used by Alembic.
|
|
revision = '001'
|
|
down_revision = None
|
|
branch_labels = None
|
|
depends_on = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
# Create users table
|
|
op.create_table('users',
|
|
sa.Column('id', sa.Integer(), nullable=False),
|
|
sa.Column('email', sa.String(), nullable=False),
|
|
sa.Column('hashed_password', sa.String(), nullable=False),
|
|
sa.Column('full_name', sa.String(), nullable=False),
|
|
sa.Column('is_active', sa.Boolean(), nullable=True),
|
|
sa.Column('is_verified', sa.Boolean(), nullable=True),
|
|
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=True),
|
|
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=True),
|
|
sa.Column('kyc_level', sa.Integer(), nullable=True),
|
|
sa.Column('phone_number', sa.String(), nullable=True),
|
|
sa.Column('country', sa.String(), nullable=True),
|
|
sa.PrimaryKeyConstraint('id')
|
|
)
|
|
op.create_index(op.f('ix_users_email'), 'users', ['email'], unique=True)
|
|
op.create_index(op.f('ix_users_id'), 'users', ['id'], unique=False)
|
|
|
|
# Create wallets table
|
|
op.create_table('wallets',
|
|
sa.Column('id', sa.Integer(), nullable=False),
|
|
sa.Column('user_id', sa.Integer(), nullable=False),
|
|
sa.Column('currency', sa.String(), nullable=False),
|
|
sa.Column('address', sa.String(), nullable=False),
|
|
sa.Column('private_key_encrypted', sa.String(), nullable=False),
|
|
sa.Column('balance', sa.Float(), nullable=True),
|
|
sa.Column('is_active', sa.Boolean(), nullable=True),
|
|
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=True),
|
|
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=True),
|
|
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
|
|
sa.PrimaryKeyConstraint('id')
|
|
)
|
|
op.create_index(op.f('ix_wallets_address'), 'wallets', ['address'], unique=True)
|
|
op.create_index(op.f('ix_wallets_id'), 'wallets', ['id'], unique=False)
|
|
|
|
# Create fiat_accounts table
|
|
op.create_table('fiat_accounts',
|
|
sa.Column('id', sa.Integer(), nullable=False),
|
|
sa.Column('user_id', sa.Integer(), nullable=False),
|
|
sa.Column('currency', sa.String(), nullable=False),
|
|
sa.Column('balance', sa.Float(), nullable=True),
|
|
sa.Column('is_active', sa.Boolean(), nullable=True),
|
|
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=True),
|
|
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=True),
|
|
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
|
|
sa.PrimaryKeyConstraint('id')
|
|
)
|
|
op.create_index(op.f('ix_fiat_accounts_id'), 'fiat_accounts', ['id'], unique=False)
|
|
|
|
# Create transactions table
|
|
op.create_table('transactions',
|
|
sa.Column('id', sa.Integer(), nullable=False),
|
|
sa.Column('user_id', sa.Integer(), nullable=False),
|
|
sa.Column('from_wallet_id', sa.Integer(), nullable=True),
|
|
sa.Column('to_wallet_id', sa.Integer(), nullable=True),
|
|
sa.Column('transaction_hash', sa.String(), nullable=True),
|
|
sa.Column('amount', sa.Float(), nullable=False),
|
|
sa.Column('fee', sa.Float(), nullable=True),
|
|
sa.Column('currency', sa.String(), nullable=False),
|
|
sa.Column('transaction_type', sa.Enum('SEND', 'RECEIVE', 'DEPOSIT', 'WITHDRAWAL', name='transactiontype'), nullable=False),
|
|
sa.Column('status', sa.Enum('PENDING', 'CONFIRMED', 'FAILED', 'CANCELLED', name='transactionstatus'), nullable=True),
|
|
sa.Column('from_address', sa.String(), nullable=True),
|
|
sa.Column('to_address', sa.String(), nullable=False),
|
|
sa.Column('block_number', sa.Integer(), nullable=True),
|
|
sa.Column('confirmations', sa.Integer(), nullable=True),
|
|
sa.Column('notes', sa.Text(), nullable=True),
|
|
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=True),
|
|
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=True),
|
|
sa.ForeignKeyConstraint(['from_wallet_id'], ['wallets.id'], ),
|
|
sa.ForeignKeyConstraint(['to_wallet_id'], ['wallets.id'], ),
|
|
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
|
|
sa.PrimaryKeyConstraint('id')
|
|
)
|
|
op.create_index(op.f('ix_transactions_id'), 'transactions', ['id'], unique=False)
|
|
op.create_index(op.f('ix_transactions_transaction_hash'), 'transactions', ['transaction_hash'], unique=True)
|
|
|
|
# Create fiat_transactions table
|
|
op.create_table('fiat_transactions',
|
|
sa.Column('id', sa.Integer(), nullable=False),
|
|
sa.Column('account_id', sa.Integer(), nullable=False),
|
|
sa.Column('amount', sa.Float(), nullable=False),
|
|
sa.Column('currency', sa.String(), nullable=False),
|
|
sa.Column('transaction_type', sa.Enum('SEND', 'RECEIVE', 'DEPOSIT', 'WITHDRAWAL', name='transactiontype'), nullable=False),
|
|
sa.Column('status', sa.Enum('PENDING', 'CONFIRMED', 'FAILED', 'CANCELLED', name='transactionstatus'), nullable=True),
|
|
sa.Column('bank_reference', sa.String(), nullable=True),
|
|
sa.Column('payment_method', sa.String(), nullable=True),
|
|
sa.Column('notes', sa.Text(), nullable=True),
|
|
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=True),
|
|
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=True),
|
|
sa.ForeignKeyConstraint(['account_id'], ['fiat_accounts.id'], ),
|
|
sa.PrimaryKeyConstraint('id')
|
|
)
|
|
op.create_index(op.f('ix_fiat_transactions_id'), 'fiat_transactions', ['id'], unique=False)
|
|
|
|
|
|
def downgrade() -> None:
|
|
op.drop_index(op.f('ix_fiat_transactions_id'), table_name='fiat_transactions')
|
|
op.drop_table('fiat_transactions')
|
|
op.drop_index(op.f('ix_transactions_transaction_hash'), table_name='transactions')
|
|
op.drop_index(op.f('ix_transactions_id'), table_name='transactions')
|
|
op.drop_table('transactions')
|
|
op.drop_index(op.f('ix_fiat_accounts_id'), table_name='fiat_accounts')
|
|
op.drop_table('fiat_accounts')
|
|
op.drop_index(op.f('ix_wallets_id'), table_name='wallets')
|
|
op.drop_index(op.f('ix_wallets_address'), table_name='wallets')
|
|
op.drop_table('wallets')
|
|
op.drop_index(op.f('ix_users_id'), table_name='users')
|
|
op.drop_index(op.f('ix_users_email'), table_name='users')
|
|
op.drop_table('users') |