Skip to content

Commit 2fa03dc

Browse files
authored
Merge pull request #5 from fayzan101/WebSockets
Websockets for inventory,product,supplier and warehouse
2 parents 4c1c677 + 8274d31 commit 2fa03dc

26 files changed

+2699
-172
lines changed

.env.example

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
DB_NAME=ims
33
DB_USER=postgres
44
DB_PASSWORD=your_secure_password_here
5-
DB_PORT=5432
5+
DB_PORT=5433
66

77
# Application Configuration
88
APP_PORT=3000

cmd/root.go

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,6 @@ import (
1212
"github.com/spf13/cobra"
1313
)
1414

15-
16-
17-
// rootCmd represents the base command when called without any subcommands
1815
var rootCmd = &cobra.Command{
1916
Use: "main.go",
2017
Short: "A brief description of your application",
@@ -24,13 +21,8 @@ examples and usage of using your application. For example:
2421
Cobra is a CLI library for Go that empowers applications.
2522
This application is a tool to generate the needed files
2623
to quickly create a Cobra application.`,
27-
// Uncomment the following line if your bare application
28-
// has an action associated with it:
29-
// Run: func(cmd *cobra.Command, args []string) { },
3024
}
3125

32-
// Execute adds all child commands to the root command and sets flags appropriately.
33-
// This is called by main.main(). It only needs to happen once to the rootCmd.
3426
func Execute() {
3527
err := rootCmd.Execute()
3628
if err != nil {
@@ -39,15 +31,5 @@ func Execute() {
3931
}
4032

4133
func init() {
42-
// Here you will define your flags and configuration settings.
43-
// Cobra supports persistent flags, which, if defined here,
44-
// will be global for your application.
45-
46-
// rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.main.go.yaml)")
47-
48-
// Cobra also supports local flags, which will only run
49-
// when this action is called directly.
5034
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
5135
}
52-
53-

configs/config.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1 @@
1-
// Package configs contains configuration loading logic.
21
package configs
3-
4-
// Add config loading functions here (e.g., LoadConfig)

docker-compose.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
version: '3.8'
2-
31
services:
42
# PostgreSQL Database Service
53
postgres:
@@ -10,7 +8,7 @@ services:
108
POSTGRES_USER: ${DB_USER}
119
POSTGRES_PASSWORD: ${DB_PASSWORD}
1210
ports:
13-
- "${DB_PORT:-5432}:5432"
11+
- "${DB_PORT:-5433}:5432"
1412
volumes:
1513
- postgres_data:/var/lib/postgresql/data
1614
- ./migrations:/docker-entrypoint-initdb.d

docker-setup.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ function StartServices {
5050
if ($LASTEXITCODE -eq 0) {
5151
Write-Host "`nServices started successfully!" -ForegroundColor Green
5252
Write-Host "Application running at: http://localhost:3000" -ForegroundColor Green
53-
Write-Host "Database: postgres://postgres@localhost:5432/ims" -ForegroundColor Green
53+
Write-Host "Database: postgres://postgres@localhost:5433/ims" -ForegroundColor Green
5454
} else {
5555
Write-Host "Failed to start services!" -ForegroundColor Red
5656
exit 1

docs/WEBSOCKET_DOCUMENTATION.md

Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
# WebSocket Real-Time Inventory Updates
2+
3+
## Overview
4+
This implementation provides real-time inventory updates using WebSockets. When inventory changes occur, all connected clients receive instant notifications about stock adjustments and low stock alerts.
5+
6+
## Features
7+
8+
### 1. Real-Time Inventory Updates
9+
- Instant notifications when inventory is adjusted
10+
- Broadcasts to all connected clients simultaneously
11+
- Includes product ID, warehouse ID, quantity, and action type
12+
13+
### 2. Low Stock Alerts
14+
- Automatic alerts when stock falls below minimum threshold
15+
- Includes product name and current vs. minimum stock levels
16+
- Can trigger browser notifications
17+
18+
### 3. Multi-Client Support
19+
- Supports unlimited concurrent WebSocket connections
20+
- Thread-safe connection management
21+
- Automatic reconnection handling
22+
23+
## WebSocket Endpoint
24+
25+
**URL:** `ws://localhost:3000/ws/inventory`
26+
27+
**Protocol:** WebSocket (ws://)
28+
29+
## Message Types
30+
31+
### 1. Inventory Update
32+
Sent when inventory is created, updated, or adjusted.
33+
34+
```json
35+
{
36+
"type": "inventory_update",
37+
"inventory_id": 123,
38+
"product_id": 45,
39+
"warehouse_id": 2,
40+
"quantity": 150,
41+
"action": "adjusted",
42+
"timestamp": "2026-02-13T10:30:45Z"
43+
}
44+
```
45+
46+
**Actions:**
47+
- `created` - New inventory record created
48+
- `updated` - Existing inventory updated
49+
- `adjusted` - Inventory adjusted via `/inventory/adjust` endpoint
50+
- `deleted` - Inventory record deleted
51+
52+
### 2. Low Stock Alert
53+
Sent when inventory quantity falls at or below the minimum stock level.
54+
55+
```json
56+
{
57+
"type": "low_stock_alert",
58+
"product_id": 45,
59+
"warehouse_id": 2,
60+
"current_quantity": 8,
61+
"min_stock": 10,
62+
"product_name": "Widget A",
63+
"timestamp": "2026-02-13T10:30:45Z"
64+
}
65+
```
66+
67+
## Usage
68+
69+
### JavaScript Client Example
70+
71+
```javascript
72+
// Connect to WebSocket
73+
const ws = new WebSocket('ws://localhost:3000/ws/inventory');
74+
75+
// Connection opened
76+
ws.onopen = () => {
77+
console.log('Connected to inventory updates');
78+
};
79+
80+
// Listen for messages
81+
ws.onmessage = (event) => {
82+
const data = JSON.parse(event.data);
83+
84+
if (data.type === 'inventory_update') {
85+
console.log(`Inventory updated: Product ${data.product_id}, Quantity: ${data.quantity}`);
86+
} else if (data.type === 'low_stock_alert') {
87+
console.log(`Low stock alert: ${data.product_name} - ${data.current_quantity}/${data.min_stock}`);
88+
}
89+
};
90+
91+
// Handle errors
92+
ws.onerror = (error) => {
93+
console.error('WebSocket error:', error);
94+
};
95+
96+
// Connection closed
97+
ws.onclose = () => {
98+
console.log('Disconnected from server');
99+
};
100+
```
101+
102+
### Demo HTML Client
103+
104+
A fully functional demo HTML client is available at:
105+
**`web/inventory-realtime.html`**
106+
107+
To use:
108+
1. Start the server: `go run main.go`
109+
2. Open `web/inventory-realtime.html` in your browser
110+
3. Click "Connect" to establish WebSocket connection
111+
4. Make inventory changes via API endpoints to see real-time updates
112+
113+
## Testing the WebSocket
114+
115+
### Step 1: Start the Server
116+
```bash
117+
go mod tidy # Install dependencies including gorilla/websocket
118+
go run main.go
119+
```
120+
121+
### Step 2: Connect a Client
122+
Open the demo HTML client or create your own WebSocket connection to `ws://localhost:3000/ws/inventory`
123+
124+
### Step 3: Trigger Updates
125+
Use the REST API to make inventory changes:
126+
127+
```bash
128+
# Adjust inventory (will trigger WebSocket broadcast)
129+
curl -X POST http://localhost:3000/inventory/adjust \
130+
-H "Content-Type: application/json" \
131+
-d '{
132+
"product_id": 1,
133+
"warehouse_id": 1,
134+
"quantity": -5,
135+
"reason": "Sale"
136+
}'
137+
```
138+
139+
### Step 4: Observe Real-Time Updates
140+
All connected WebSocket clients will receive the update instantly!
141+
142+
## Architecture
143+
144+
### Components
145+
146+
1. **Hub (`internal/websocket/hub.go`)**
147+
- Manages all active WebSocket connections
148+
- Broadcasts messages to all clients
149+
- Handles client registration/unregistration
150+
- Thread-safe operations using mutex
151+
152+
2. **Client (`internal/websocket/client.go`)**
153+
- Represents individual WebSocket connection
154+
- Handles read/write operations
155+
- Implements ping/pong for connection health
156+
- Automatic cleanup on disconnect
157+
158+
3. **Handler (`internal/websocket/handler.go`)**
159+
- HTTP to WebSocket upgrade
160+
- Global hub instance management
161+
- Connection request handling
162+
163+
4. **Integration (`internal/inventory/handlers.go`)**
164+
- Broadcasts updates after inventory changes
165+
- Checks for low stock conditions
166+
- Integrates seamlessly with existing handlers
167+
168+
## Configuration
169+
170+
### Connection Settings
171+
Defined in `internal/websocket/client.go`:
172+
173+
```go
174+
writeWait = 10 * time.Second // Write timeout
175+
pongWait = 60 * time.Second // Read timeout
176+
pingPeriod = 54 * time.Second // Ping interval
177+
maxMessageSize = 512 // Max message size
178+
```
179+
180+
### CORS Settings
181+
Currently allows all origins (development mode). For production, modify `internal/websocket/handler.go`:
182+
183+
```go
184+
var upgrader = websocket.Upgrader{
185+
CheckOrigin: func(r *http.Request) bool {
186+
// Restrict to specific origins in production
187+
origin := r.Header.Get("Origin")
188+
return origin == "https://yourdomain.com"
189+
},
190+
}
191+
```
192+
193+
## Production Considerations
194+
195+
1. **CORS Configuration**
196+
- Restrict `CheckOrigin` to trusted domains
197+
- Implement authentication tokens
198+
199+
2. **Rate Limiting**
200+
- Add rate limiting to prevent abuse
201+
- Limit connections per IP/user
202+
203+
3. **Message Filtering**
204+
- Allow clients to subscribe to specific products/warehouses
205+
- Reduce unnecessary bandwidth
206+
207+
4. **Monitoring**
208+
- Track active connections count
209+
- Monitor message throughput
210+
- Log connection/disconnection events
211+
212+
5. **SSL/TLS**
213+
- Use `wss://` instead of `ws://` in production
214+
- Implement proper certificate management
215+
216+
## Troubleshooting
217+
218+
### WebSocket Connection Fails
219+
- Ensure server is running on port 3000
220+
- Check firewall settings
221+
- Verify URL is correct: `ws://localhost:3000/ws/inventory`
222+
223+
### No Updates Received
224+
- Verify WebSocket connection is established
225+
- Check server logs for broadcast messages
226+
- Ensure inventory changes are being made through the API
227+
228+
### Connection Drops
229+
- Check network stability
230+
- Increase `pongWait` timeout if needed
231+
- Implement client-side reconnection logic
232+
233+
## Future Enhancements
234+
235+
- [ ] Client-side filtering (subscribe to specific products)
236+
- [ ] Authentication/Authorization
237+
- [ ] Message history/replay
238+
- [ ] Compression for large broadcasts
239+
- [ ] Horizontal scaling with Redis pub/sub
240+
- [ ] WebSocket connection metrics dashboard
241+
242+
## Dependencies
243+
244+
- **gorilla/websocket** v1.5.3 - High-performance WebSocket library
245+
246+
Install with:
247+
```bash
248+
go get github.com/gorilla/websocket@v1.5.3
249+
go mod tidy
250+
```

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module myapp
33
go 1.25.1
44

55
require (
6+
github.com/gorilla/websocket v1.5.3
67
github.com/joho/godotenv v1.5.1
78
github.com/spf13/cobra v1.10.1
89
gorm.io/driver/postgres v1.6.0

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6N
22
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
33
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
44
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
5+
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
6+
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
57
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
68
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
79
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=

internal/db.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ func InitDB(connStr string) {
1717
log.Fatalf("Failed to connect to database: %v", err)
1818
}
1919
log.Println("Connected to PostgreSQL database with GORM.")
20-
21-
// Auto-migrate all IMS models
2220
if err := DB.AutoMigrate(
2321
&Product{},
2422
&Warehouse{},
@@ -35,8 +33,6 @@ func InitDB(connStr string) {
3533
}
3634
log.Println("Database migration completed successfully.")
3735
}
38-
39-
// LogAudit creates an audit log entry
4036
func LogAudit(action, entity string, entityID uint, userID, details string) {
4137
log := AuditLog{
4238
Action: action,

0 commit comments

Comments
 (0)