completed SMS gateway project

This commit is contained in:
Min Zeya Phyo
2025-10-22 17:22:17 +08:00
commit c883fa7128
190 changed files with 16294 additions and 0 deletions

183
PERMISSIONS_FIX.md Normal file
View File

@@ -0,0 +1,183 @@
# Permissions Field Fix
## Issue
```
undefined method 'stringify_keys' for an instance of String
```
This error occurred in the API Keys index view when trying to display permissions.
## Root Cause
The `permissions` field in the `api_keys` table is a `jsonb` column (PostgreSQL native JSON type). Rails handles this automatically, but the code was:
1. Not properly handling the `permissions` attribute
2. Not providing safe fallbacks for nil or invalid data
## Solution Applied
### 1. Updated ApiKey Model
**File:** `app/models/api_key.rb`
Added a safe `permissions` method that:
- Returns empty hash if permissions is nil
- Returns the value if it's already a Hash
- Parses JSON if it's a String
- Returns empty hash on any error
```ruby
# Ensure permissions is always a hash
def permissions
value = read_attribute(:permissions)
return {} if value.nil?
return value if value.is_a?(Hash)
return JSON.parse(value) if value.is_a?(String)
{}
rescue JSON::ParserError
{}
end
```
### 2. Updated Views
**Files:**
- `app/views/admin/api_keys/index.html.erb`
- `app/views/admin/api_keys/show.html.erb`
Added defensive code to handle edge cases:
```erb
<% perms = api_key.permissions || {} %>
<% perms = {} unless perms.is_a?(Hash) %>
<% if perms.any? %>
<% perms.select { |_, v| v }.keys.each do |perm| %>
<span><%= perm.to_s.humanize %></span>
<% end %>
<% else %>
<span>None</span>
<% end %>
```
## Why This Works
### PostgreSQL JSONB Support
- Rails 5+ has native support for PostgreSQL JSONB columns
- No need for `serialize` declaration
- Data is stored and retrieved as Hash automatically
- But we need to handle edge cases
### Safe Accessor Method
- The custom `permissions` method ensures we always get a Hash
- Handles legacy data or corrupted entries
- Provides sensible defaults
### View Defensive Coding
- Checks for nil before using
- Verifies it's a Hash
- Gracefully degrades to "None" if empty
## Database Schema
The permissions column is properly defined:
```ruby
create_table "api_keys" do |t|
# ...
t.jsonb "permissions", default: {}
# ...
end
```
**Key points:**
- Type: `jsonb` (not `json` or `text`)
- Default: `{}` (empty hash)
- No serialization needed
## Verification
Test that permissions work correctly:
```bash
bin/rails runner "
api_key = ApiKey.first
puts 'Permissions class: ' + api_key.permissions.class.to_s
puts 'Permissions value: ' + api_key.permissions.inspect
puts 'Can check permission: ' + api_key.can?('send_sms').to_s
"
```
Should output:
```
Permissions class: Hash
Permissions value: {"send_sms"=>true, "receive_sms"=>true, ...}
Can check permission: true
```
## Related Code
### Creating API Keys
The create action already passes a hash:
```ruby
permissions = {}
permissions["send_sms"] = params[:api_key][:send_sms] == "1"
permissions["receive_sms"] = params[:api_key][:receive_sms] == "1"
permissions["manage_gateways"] = params[:api_key][:manage_gateways] == "1"
ApiKey.generate!(
name: params[:api_key][:name],
permissions: permissions,
expires_at: expires_at
)
```
### Checking Permissions
The `can?` method works with our safe accessor:
```ruby
def can?(permission)
permissions.fetch(permission.to_s, false)
end
```
Usage:
```ruby
api_key.can?("send_sms") # => true or false
```
## Testing
### Manual Test
1. Start server: `bin/dev`
2. Visit: http://localhost:3000/admin/api_keys
3. Should see permissions displayed as badges
4. Create new API key
5. Check permissions are saved and displayed
### Console Test
```ruby
# In rails console
api_key = ApiKey.first
# Should return Hash
api_key.permissions
# => {"send_sms"=>true, "receive_sms"=>true}
# Should work
api_key.can?("send_sms")
# => true
# Should handle missing permissions
api_key.can?("nonexistent")
# => false
```
## Summary
**Fixed**: Permissions now always return a Hash
**Fixed**: Views handle nil/invalid permissions gracefully
**Improved**: Added defensive coding throughout
**Maintained**: PostgreSQL native JSONB support
**Tested**: All API keys work correctly
The admin interface can now safely display API keys and their permissions without errors!