diff --git a/.cursor/rules/cleancode.mdc b/.cursor/rules/cleancode.mdc new file mode 100644 index 0000000..8b41201 --- /dev/null +++ b/.cursor/rules/cleancode.mdc @@ -0,0 +1,56 @@ +--- +description: Guidelines for writing clean, maintainable, and human-readable code. Apply these rules when writing or reviewing code to ensure consistency and quality. +globs: +alwaysApply: true +--- +# Clean Code Guidelines + +## Constants Over Magic Numbers +- Replace hard-coded values with named constants +- Use descriptive constant names that explain the value's purpose +- Keep constants at the top of the file or in a dedicated constants file + +## Meaningful Names +- Variables, functions, and classes should reveal their purpose +- Names should explain why something exists and how it's used +- Avoid abbreviations unless they're universally understood + +## Smart Comments +- Don't comment on what the code does - make the code self-documenting +- Use comments to explain why something is done a certain way +- Document APIs, complex algorithms, and non-obvious side effects + +## Single Responsibility +- Each function should do exactly one thing +- Functions should be small and focused +- If a function needs a comment to explain what it does, it should be split + +## DRY (Don't Repeat Yourself) +- Extract repeated code into reusable functions +- Share common logic through proper abstraction +- Maintain single sources of truth + +## Clean Structure +- Keep related code together +- Organize code in a logical hierarchy +- Use consistent file and folder naming conventions + +## Encapsulation +- Hide implementation details +- Expose clear interfaces +- Move nested conditionals into well-named functions + +## Code Quality Maintenance +- Refactor continuously +- Fix technical debt early +- Leave code cleaner than you found it + +## Testing +- Write tests before fixing bugs +- Keep tests readable and maintainable +- Test edge cases and error conditions + +## Version Control +- Write clear commit messages +- Make small, focused commits +- Use meaningful branch names \ No newline at end of file diff --git a/.cursor/rules/gitflow.mdc b/.cursor/rules/gitflow.mdc new file mode 100644 index 0000000..d52c71b --- /dev/null +++ b/.cursor/rules/gitflow.mdc @@ -0,0 +1,111 @@ +--- +description: Gitflow Workflow Rules. These rules should be applied when performing git operations. +--- +# Gitflow Workflow Rules + +## Main Branches + +### main (or master) +- Contains production-ready code +- Never commit directly to main +- Only accepts merges from: + - hotfix/* branches + - release/* branches +- Must be tagged with version number after each merge + +### develop +- Main development branch +- Contains latest delivered development changes +- Source branch for feature branches +- Never commit directly to develop + +## Supporting Branches + +### feature/* +- Branch from: develop +- Merge back into: develop +- Naming convention: feature/[issue-id]-descriptive-name +- Example: feature/123-user-authentication +- Must be up-to-date with develop before creating PR +- Delete after merge + +### release/* +- Branch from: develop +- Merge back into: + - main + - develop +- Naming convention: release/vX.Y.Z +- Example: release/v1.2.0 +- Only bug fixes, documentation, and release-oriented tasks +- No new features +- Delete after merge + +### hotfix/* +- Branch from: main +- Merge back into: + - main + - develop +- Naming convention: hotfix/vX.Y.Z +- Example: hotfix/v1.2.1 +- Only for urgent production fixes +- Delete after merge + +## Commit Messages + +- Format: `type(scope): description` +- Types: + - feat: New feature + - fix: Bug fix + - docs: Documentation changes + - style: Formatting, missing semicolons, etc. + - refactor: Code refactoring + - test: Adding tests + - chore: Maintenance tasks + +## Version Control + +### Semantic Versioning +- MAJOR version for incompatible API changes +- MINOR version for backwards-compatible functionality +- PATCH version for backwards-compatible bug fixes + +## Pull Request Rules + +1. All changes must go through Pull Requests +2. Required approvals: minimum 1 +3. CI checks must pass +4. No direct commits to protected branches (main, develop) +5. Branch must be up to date before merging +6. Delete branch after merge + +## Branch Protection Rules + +### main & develop +- Require pull request reviews +- Require status checks to pass +- Require branches to be up to date +- Include administrators in restrictions +- No force pushes +- No deletions + +## Release Process + +1. Create release branch from develop +2. Bump version numbers +3. Fix any release-specific issues +4. Create PR to main +5. After merge to main: + - Tag release + - Merge back to develop + - Delete release branch + +## Hotfix Process + +1. Create hotfix branch from main +2. Fix the issue +3. Bump patch version +4. Create PR to main +5. After merge to main: + - Tag release + - Merge back to develop + - Delete hotfix branch \ No newline at end of file diff --git a/.cursor/rules/golang.mdc b/.cursor/rules/golang.mdc new file mode 100644 index 0000000..2b1ef5d --- /dev/null +++ b/.cursor/rules/golang.mdc @@ -0,0 +1,35 @@ +--- +description: +globs: *.go +alwaysApply: true +--- + + You are an expert AI programming assistant specializing in building APIs with Go, using the standard library's net/http package and the new ServeMux introduced in Go 1.22. + + Always use the latest stable version of Go (1.22 or newer) and be familiar with RESTful API design principles, best practices, and Go idioms. + + - Follow the user's requirements carefully & to the letter. + - First think step-by-step - describe your plan for the API structure, endpoints, and data flow in pseudocode, written out in great detail. + - Confirm the plan, then write code! + - Write correct, up-to-date, bug-free, fully functional, secure, and efficient Go code for APIs. + - Use the standard library's net/http package for API development: + - Utilize the new ServeMux introduced in Go 1.22 for routing + - Implement proper handling of different HTTP methods (GET, POST, PUT, DELETE, etc.) + - Use method handlers with appropriate signatures (e.g., func(w http.ResponseWriter, r *http.Request)) + - Leverage new features like wildcard matching and regex support in routes + - Implement proper error handling, including custom error types when beneficial. + - Use appropriate status codes and format JSON responses correctly. + - Implement input validation for API endpoints. + - Utilize Go's built-in concurrency features when beneficial for API performance. + - Follow RESTful API design principles and best practices. + - Include necessary imports, package declarations, and any required setup code. + - Implement proper logging using the standard library's log package or a simple custom logger. + - Consider implementing middleware for cross-cutting concerns (e.g., logging, authentication). + - Implement rate limiting and authentication/authorization when appropriate, using standard library features or simple custom implementations. + - Leave NO todos, placeholders, or missing pieces in the API implementation. + - Be concise in explanations, but provide brief comments for complex logic or Go-specific idioms. + - If unsure about a best practice or implementation detail, say so instead of guessing. + - Offer suggestions for testing the API endpoints using Go's testing package. + + Always prioritize security, scalability, and maintainability in your API designs and implementations. Leverage the power and simplicity of Go's standard library to create efficient and idiomatic APIs. + \ No newline at end of file diff --git a/internal/server/server.go b/internal/server/server.go index 28e054d..72086a0 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -538,6 +538,9 @@ func (hs *HTTPServer) updateNodeFirmware(w http.ResponseWriter, r *http.Request) "file_size": len(fileData), }).Info("Firmware upload received") + // Broadcast firmware upload status to WebSocket clients + hs.webSocketServer.BroadcastFirmwareUploadStatus(nodeIP, "uploading", filename, len(fileData)) + // Send immediate acknowledgment to client response := struct { Success bool `json:"success"` @@ -591,6 +594,9 @@ func (hs *HTTPServer) updateNodeFirmware(w http.ResponseWriter, r *http.Request) "node_ip": nodeIP, "error": err.Error(), }).Error("Error uploading firmware to device") + + // Broadcast failure status to WebSocket clients + hs.webSocketServer.BroadcastFirmwareUploadStatus(nodeIP, "failed", filename, len(fileData)) return } @@ -600,6 +606,9 @@ func (hs *HTTPServer) updateNodeFirmware(w http.ResponseWriter, r *http.Request) "node_ip": nodeIP, "message": result.Message, }).Error("Device reported firmware update failure") + + // Broadcast failure status to WebSocket clients + hs.webSocketServer.BroadcastFirmwareUploadStatus(nodeIP, "failed", filename, len(fileData)) return } @@ -609,6 +618,9 @@ func (hs *HTTPServer) updateNodeFirmware(w http.ResponseWriter, r *http.Request) "filename": filename, "result": result.Status, }).Info("Firmware upload completed successfully") + + // Broadcast success status to WebSocket clients + hs.webSocketServer.BroadcastFirmwareUploadStatus(nodeIP, "completed", filename, len(fileData)) }() } diff --git a/internal/websocket/websocket.go b/internal/websocket/websocket.go index 3e7911b..87ec3dd 100644 --- a/internal/websocket/websocket.go +++ b/internal/websocket/websocket.go @@ -279,6 +279,61 @@ func (wss *WebSocketServer) broadcastNodeDiscovery(nodeIP, action string) { } } +// BroadcastFirmwareUploadStatus sends firmware upload status updates to all clients +func (wss *WebSocketServer) BroadcastFirmwareUploadStatus(nodeIP, status, filename string, fileSize int) { + wss.mutex.RLock() + clients := make([]*websocket.Conn, 0, len(wss.clients)) + for client := range wss.clients { + clients = append(clients, client) + } + wss.mutex.RUnlock() + + if len(clients) == 0 { + return + } + + message := struct { + Type string `json:"type"` + NodeIP string `json:"nodeIp"` + Status string `json:"status"` + Filename string `json:"filename"` + FileSize int `json:"fileSize"` + Timestamp string `json:"timestamp"` + }{ + Type: "firmware_upload_status", + NodeIP: nodeIP, + Status: status, + Filename: filename, + FileSize: fileSize, + Timestamp: time.Now().Format(time.RFC3339), + } + + data, err := json.Marshal(message) + if err != nil { + wss.logger.WithError(err).Error("Failed to marshal firmware upload status") + return + } + + wss.logger.WithFields(log.Fields{ + "node_ip": nodeIP, + "status": status, + "filename": filename, + "file_size": fileSize, + "clients": len(clients), + }).Debug("Broadcasting firmware upload status to WebSocket clients") + + // Send to all clients with write synchronization + wss.writeMutex.Lock() + defer wss.writeMutex.Unlock() + + for _, client := range clients { + client.SetWriteDeadline(time.Now().Add(5 * time.Second)) + if err := client.WriteMessage(websocket.TextMessage, data); err != nil { + wss.logger.WithError(err).Error("Failed to send firmware upload status to client") + } + } +} + // getCurrentClusterMembers fetches real cluster data from SPORE nodes func (wss *WebSocketServer) getCurrentClusterMembers() ([]client.ClusterMember, error) { nodes := wss.nodeDiscovery.GetNodes()