completed SMS gateway project
This commit is contained in:
430
CLAUDE.md
Normal file
430
CLAUDE.md
Normal file
@@ -0,0 +1,430 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
MySMSAPio is an SMS Gateway Backend API - a Rails 8.0 REST API and WebSocket server for managing SMS messaging through Android gateway devices. It provides programmatic SMS capabilities with OTP management, webhooks, and real-time WebSocket communication.
|
||||
|
||||
The project includes a web-based admin interface for managing API keys, monitoring SMS logs, and viewing gateway status. See [ADMIN_INTERFACE.md](ADMIN_INTERFACE.md) for detailed documentation.
|
||||
|
||||
## Tech Stack
|
||||
|
||||
- **Framework**: Rails 8.0.3 (API-only mode)
|
||||
- **Ruby**: 3.4.7
|
||||
- **Database**: PostgreSQL 14+
|
||||
- **Cache/Queue**: Redis 7+
|
||||
- **Background Jobs**: Sidekiq 7 with sidekiq-cron
|
||||
- **WebSocket**: Action Cable (Redis adapter)
|
||||
- **Authentication**: API Keys with SHA256 hashing
|
||||
- **Server**: Puma with Thruster for production
|
||||
- **Deployment**: Kamal (Docker-based deployment)
|
||||
- **Code Quality**: RuboCop with rails-omakase configuration, Brakeman for security
|
||||
|
||||
## SMS Gateway Dependencies
|
||||
|
||||
- **redis**: WebSocket, caching, background jobs
|
||||
- **sidekiq**: Background job processing
|
||||
- **sidekiq-cron**: Scheduled jobs (health checks, cleanup)
|
||||
- **jwt**: API authentication tokens
|
||||
- **rack-cors**: Cross-origin resource sharing
|
||||
- **phonelib**: Phone number validation
|
||||
- **rotp**: OTP generation
|
||||
- **httparty**: Webhook HTTP requests
|
||||
- **pagy**: Pagination
|
||||
|
||||
## Development Commands
|
||||
|
||||
### Initial Setup
|
||||
```bash
|
||||
bin/setup
|
||||
```
|
||||
This will:
|
||||
- Install dependencies
|
||||
- Prepare the database
|
||||
- Clear logs and temp files
|
||||
- Start the development server
|
||||
|
||||
To skip auto-starting the server:
|
||||
```bash
|
||||
bin/setup --skip-server
|
||||
```
|
||||
|
||||
### Running the Application
|
||||
```bash
|
||||
bin/dev
|
||||
# or
|
||||
bin/rails server
|
||||
```
|
||||
|
||||
### Database
|
||||
|
||||
**Create and migrate databases:**
|
||||
```bash
|
||||
bin/rails db:create
|
||||
bin/rails db:migrate
|
||||
```
|
||||
|
||||
**Prepare database (create + migrate + seed):**
|
||||
```bash
|
||||
bin/rails db:prepare
|
||||
```
|
||||
|
||||
**Reset database:**
|
||||
```bash
|
||||
bin/rails db:reset
|
||||
```
|
||||
|
||||
**Database console:**
|
||||
```bash
|
||||
bin/rails dbconsole
|
||||
# or via Kamal:
|
||||
bin/kamal dbc
|
||||
```
|
||||
|
||||
### Testing
|
||||
|
||||
**Run all tests:**
|
||||
```bash
|
||||
bin/rails test
|
||||
```
|
||||
|
||||
**Run specific test file:**
|
||||
```bash
|
||||
bin/rails test test/models/your_model_test.rb
|
||||
```
|
||||
|
||||
**Run specific test by line number:**
|
||||
```bash
|
||||
bin/rails test test/models/your_model_test.rb:42
|
||||
```
|
||||
|
||||
**System tests:**
|
||||
```bash
|
||||
bin/rails test:system
|
||||
```
|
||||
|
||||
Tests run in parallel using all processor cores by default.
|
||||
|
||||
### Code Quality
|
||||
|
||||
**Run RuboCop:**
|
||||
```bash
|
||||
bin/rubocop
|
||||
```
|
||||
|
||||
**Auto-correct RuboCop violations:**
|
||||
```bash
|
||||
bin/rubocop -a
|
||||
```
|
||||
|
||||
**Run Brakeman security scanner:**
|
||||
```bash
|
||||
bin/brakeman
|
||||
```
|
||||
|
||||
### Rails Console
|
||||
```bash
|
||||
bin/rails console
|
||||
# or via Kamal:
|
||||
bin/kamal console
|
||||
```
|
||||
|
||||
### Background Jobs (Sidekiq)
|
||||
|
||||
**Start Sidekiq:**
|
||||
```bash
|
||||
bundle exec sidekiq
|
||||
```
|
||||
|
||||
**View scheduled jobs:**
|
||||
```bash
|
||||
bundle exec sidekiq-cron
|
||||
```
|
||||
|
||||
**Scheduled jobs run automatically**:
|
||||
- `CheckGatewayHealthJob`: Every minute
|
||||
- `CleanupExpiredOtpsJob`: Every 15 minutes
|
||||
- `ResetDailyCountersJob`: Daily at midnight
|
||||
|
||||
### Asset Management
|
||||
|
||||
**Precompile assets:**
|
||||
```bash
|
||||
bin/rails assets:precompile
|
||||
```
|
||||
|
||||
**Clear compiled assets:**
|
||||
```bash
|
||||
bin/rails assets:clobber
|
||||
```
|
||||
|
||||
### Deployment (Kamal)
|
||||
|
||||
**Deploy application:**
|
||||
```bash
|
||||
bin/kamal deploy
|
||||
```
|
||||
|
||||
**View logs:**
|
||||
```bash
|
||||
bin/kamal logs
|
||||
```
|
||||
|
||||
**Access remote shell:**
|
||||
```bash
|
||||
bin/kamal shell
|
||||
```
|
||||
|
||||
**View configuration:**
|
||||
```bash
|
||||
bin/kamal config
|
||||
```
|
||||
|
||||
The deployment configuration is in `config/deploy.yml`. Production uses Docker containers with SSL enabled via Let's Encrypt.
|
||||
|
||||
## Database Architecture
|
||||
|
||||
### Development/Test
|
||||
Single PostgreSQL database per environment:
|
||||
- Development: `my_smsa_pio_development`
|
||||
- Test: `my_smsa_pio_test`
|
||||
|
||||
### Production
|
||||
Multi-database setup for performance isolation:
|
||||
- **Primary**: Main application data
|
||||
- **Cache**: Database-backed cache via Solid Cache (migrations in `db/cache_migrate/`)
|
||||
- **Queue**: Job queue via Solid Queue (migrations in `db/queue_migrate/`)
|
||||
- **Cable**: WebSocket connections via Solid Cable (migrations in `db/cable_migrate/`)
|
||||
|
||||
Each database shares connection pooling configuration but maintains separate migration paths.
|
||||
|
||||
## Application Structure
|
||||
|
||||
The application follows standard Rails 8 conventions with the modern "Omakase" stack:
|
||||
|
||||
- **app/**: Standard Rails application code (models, controllers, views, jobs, mailers, helpers)
|
||||
- **config/**: Configuration files including multi-database setup
|
||||
- **db/**: Database schemas with separate migration directories for each database role
|
||||
- **lib/**: Custom library code (autoloaded via `config.autoload_lib`)
|
||||
- **test/**: Minitest-based test suite with system tests using Capybara + Selenium
|
||||
|
||||
## Key Configuration Details
|
||||
|
||||
### Module Name
|
||||
The Rails application module is `MySmsaPio` (defined in `config/application.rb`).
|
||||
|
||||
### Environment Variables
|
||||
|
||||
**Required**:
|
||||
- `DATABASE_URL`: PostgreSQL connection string
|
||||
- `REDIS_URL`: Redis connection string (used for Action Cable, Sidekiq, caching)
|
||||
- `SECRET_KEY_BASE`: Rails secret key
|
||||
- `RAILS_ENV`: Environment (development, test, production)
|
||||
|
||||
**Optional**:
|
||||
- `ALLOWED_ORIGINS`: CORS allowed origins (default: `*`)
|
||||
- `DEFAULT_COUNTRY_CODE`: Default country for phone validation (default: `US`)
|
||||
- `RAILS_LOG_LEVEL`: Logging level
|
||||
|
||||
### Docker & Production
|
||||
|
||||
The application is containerized using a multi-stage Dockerfile optimized for production:
|
||||
- Base Ruby 3.4.7 slim image
|
||||
- Uses Thruster as the web server (listens on port 80)
|
||||
- Non-root user (rails:rails, uid/gid 1000)
|
||||
- Entrypoint handles database preparation via `bin/docker-entrypoint`
|
||||
- Assets precompiled during build
|
||||
|
||||
### Code Style
|
||||
|
||||
Follow RuboCop Rails Omakase conventions. Configuration is minimal, inheriting from `rubocop-rails-omakase` gem.
|
||||
|
||||
## Health Checks
|
||||
|
||||
The application includes a health check endpoint:
|
||||
```
|
||||
GET /up
|
||||
```
|
||||
Returns 200 if app boots successfully, 500 otherwise. Used by load balancers and monitoring.
|
||||
|
||||
---
|
||||
|
||||
## SMS Gateway Specific Information
|
||||
|
||||
### Database Models
|
||||
|
||||
**Gateway** (`app/models/gateway.rb`):
|
||||
- Represents Android SMS gateway devices
|
||||
- Tracks connection status, heartbeat, message counts
|
||||
- Generates and stores API keys (hashed with SHA256)
|
||||
|
||||
**SmsMessage** (`app/models/sms_message.rb`):
|
||||
- Stores all SMS messages (inbound and outbound)
|
||||
- Auto-generates unique message IDs
|
||||
- Validates phone numbers using Phonelib
|
||||
- Triggers SendSmsJob on creation for outbound messages
|
||||
|
||||
**OtpCode** (`app/models/otp_code.rb`):
|
||||
- Generates 6-digit OTP codes
|
||||
- Enforces rate limiting (3 per phone per hour)
|
||||
- Auto-expires after 5 minutes
|
||||
- Tracks verification attempts (max 3)
|
||||
|
||||
**WebhookConfig** (`app/models/webhook_config.rb`):
|
||||
- Configures webhooks for SMS events
|
||||
- Signs payloads with HMAC-SHA256
|
||||
- Supports retry logic
|
||||
|
||||
**ApiKey** (`app/models/api_key.rb`):
|
||||
- Client API keys for application access
|
||||
- Permissions-based access control
|
||||
- Tracks usage and expiration
|
||||
|
||||
### API Controllers
|
||||
|
||||
**Gateway APIs** (`app/controllers/api/v1/gateway/`):
|
||||
- `RegistrationsController`: Register new gateway devices
|
||||
- `HeartbeatsController`: Keep-alive from gateways
|
||||
- `SmsController`: Report received SMS and delivery status
|
||||
|
||||
**Client APIs** (`app/controllers/api/v1/`):
|
||||
- `SmsController`: Send/receive SMS, check status
|
||||
- `OtpController`: Generate and verify OTP codes
|
||||
- `Admin::GatewaysController`: Manage gateway devices
|
||||
- `Admin::StatsController`: System statistics
|
||||
|
||||
### WebSocket Communication
|
||||
|
||||
**GatewayChannel** (`app/channels/gateway_channel.rb`):
|
||||
- Real-time bidirectional communication with gateway devices
|
||||
- Authenticated via API key digest
|
||||
- Handles:
|
||||
- Gateway connection/disconnection
|
||||
- Heartbeat messages
|
||||
- Delivery reports
|
||||
- Inbound SMS notifications
|
||||
- Outbound SMS commands
|
||||
|
||||
**Connection** (`app/channels/application_cable/connection.rb`):
|
||||
- Authenticates WebSocket connections
|
||||
- Verifies gateway API keys
|
||||
|
||||
### Background Jobs
|
||||
|
||||
**Processing Jobs**:
|
||||
- `SendSmsJob`: Routes outbound SMS to available gateways via WebSocket
|
||||
- `ProcessInboundSmsJob`: Triggers webhooks for received SMS
|
||||
- `RetryFailedSmsJob`: Retries failed messages with exponential backoff
|
||||
- `TriggerWebhookJob`: Executes webhook HTTP requests
|
||||
|
||||
**Scheduled Jobs** (config/sidekiq_cron.yml):
|
||||
- `CheckGatewayHealthJob`: Marks offline gateways (every minute)
|
||||
- `CleanupExpiredOtpsJob`: Deletes expired OTP codes (every 15 minutes)
|
||||
- `ResetDailyCountersJob`: Resets daily message counters (daily at midnight)
|
||||
|
||||
### Admin Web Interface
|
||||
|
||||
**Admin Authentication** (`app/models/admin.rb`, `app/controllers/admin/`):
|
||||
- Session-based authentication with bcrypt password hashing
|
||||
- Access URL: `/admin/login`
|
||||
- Default credentials (development): admin@example.com / password123
|
||||
|
||||
**Admin Features**:
|
||||
1. **Dashboard** (`/admin/dashboard`): Real-time statistics and recent activity
|
||||
2. **API Keys Management** (`/admin/api_keys`): Create, view, and revoke API keys
|
||||
3. **SMS Logs** (`/admin/logs`): Monitor messages with advanced filtering
|
||||
4. **Gateway Management** (`/admin/gateways`): View and manage gateway devices
|
||||
|
||||
**Admin Controllers**:
|
||||
- `Admin::BaseController`: Base controller with authentication
|
||||
- `Admin::SessionsController`: Login/logout
|
||||
- `Admin::DashboardController`: Dashboard with stats
|
||||
- `Admin::ApiKeysController`: API key CRUD operations
|
||||
- `Admin::LogsController`: SMS logs with filtering
|
||||
- `Admin::GatewaysController`: Gateway management
|
||||
|
||||
See [ADMIN_INTERFACE.md](ADMIN_INTERFACE.md) for complete documentation.
|
||||
|
||||
### Authentication & Security
|
||||
|
||||
**API Key Types**:
|
||||
1. **Gateway Keys** (`gw_live_...`): For Android gateway devices
|
||||
2. **Client Keys** (`api_live_...`): For application APIs
|
||||
3. **Admin Access**: Session-based web authentication
|
||||
|
||||
**Authentication Flow**:
|
||||
- API keys passed in `Authorization: Bearer <key>` header
|
||||
- Keys are hashed with SHA256 before storage
|
||||
- Admin passwords hashed with bcrypt
|
||||
- Concerns: `ApiAuthenticatable`, `RateLimitable`
|
||||
|
||||
**Rate Limiting**:
|
||||
- Implemented via Redis caching
|
||||
- OTP: Max 3 per phone per hour
|
||||
- SMS Send: 100 per minute per API key
|
||||
- Customizable per endpoint
|
||||
|
||||
### Key Files
|
||||
|
||||
**Models**: `app/models/{gateway,sms_message,otp_code,webhook_config,api_key}.rb`
|
||||
|
||||
**Controllers**: `app/controllers/api/v1/**/*_controller.rb`
|
||||
|
||||
**Jobs**: `app/jobs/{send_sms_job,process_inbound_sms_job,retry_failed_sms_job,trigger_webhook_job,check_gateway_health_job,cleanup_expired_otps_job,reset_daily_counters_job}.rb`
|
||||
|
||||
**Channels**: `app/channels/{gateway_channel,application_cable/connection}.rb`
|
||||
|
||||
**Concerns**: `app/controllers/concerns/{api_authenticatable,rate_limitable}.rb`, `app/models/concerns/metrics.rb`
|
||||
|
||||
**Config**:
|
||||
- `config/routes.rb`: API routes
|
||||
- `config/cable.yml`: Action Cable (Redis)
|
||||
- `config/sidekiq_cron.yml`: Scheduled jobs
|
||||
- `config/initializers/{cors,sidekiq,phonelib,pagy}.rb`
|
||||
|
||||
### Common Development Tasks
|
||||
|
||||
**Generate new API key**:
|
||||
```ruby
|
||||
# In Rails console
|
||||
result = ApiKey.generate!(name: "My App", permissions: { send_sms: true, receive_sms: true })
|
||||
puts result[:raw_key] # Save this immediately!
|
||||
```
|
||||
|
||||
**Register gateway manually**:
|
||||
```ruby
|
||||
# In Rails console
|
||||
gateway = Gateway.new(device_id: "my-device-001", name: "My Gateway")
|
||||
api_key = gateway.generate_api_key!
|
||||
puts api_key # Save this immediately!
|
||||
```
|
||||
|
||||
**Check gateway status**:
|
||||
```ruby
|
||||
# In Rails console
|
||||
Gateway.online.each { |g| puts "#{g.name}: #{g.status}" }
|
||||
```
|
||||
|
||||
**View pending messages**:
|
||||
```ruby
|
||||
# In Rails console
|
||||
SmsMessage.pending.count
|
||||
SmsMessage.failed.each { |msg| puts "#{msg.message_id}: #{msg.error_message}" }
|
||||
```
|
||||
|
||||
**Test WebSocket connection**:
|
||||
```bash
|
||||
# Use wscat or similar WebSocket client
|
||||
wscat -c "ws://localhost:3000/cable?api_key=gw_live_your_key_here"
|
||||
```
|
||||
|
||||
### Important Notes
|
||||
|
||||
- **Redis is required** for Action Cable, Sidekiq, and caching to work
|
||||
- All phone numbers are validated and normalized using Phonelib
|
||||
- Gateway devices must send heartbeats every 2 minutes or they'll be marked offline
|
||||
- Outbound SMS messages are queued and sent asynchronously via Sidekiq
|
||||
- Failed messages retry automatically up to 3 times with exponential backoff
|
||||
- OTP codes expire after 5 minutes and allow max 3 verification attempts
|
||||
- All API keys are SHA256 hashed - raw keys are only shown once during creation
|
||||
Reference in New Issue
Block a user