Skip to content

Commit 492605a

Browse files
FEATURE (cursor): Update cursor rules
1 parent f9eaead commit 492605a

File tree

6 files changed

+1100
-50
lines changed

6 files changed

+1100
-50
lines changed
Lines changed: 145 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,152 @@
11
---
2-
description:
3-
globs:
2+
description:
3+
globs:
44
alwaysApply: true
55
---
6+
67
Always place private methods to the bottom of file
78

8-
Code should look like:
9+
**This rule applies to ALL Go files including tests, services, controllers, repositories, etc.**
10+
11+
In Go, exported (public) functions/methods start with uppercase letters, while unexported (private) ones start with lowercase letters.
12+
13+
## Structure Order:
14+
15+
1. Type definitions and constants
16+
2. Public methods/functions (uppercase)
17+
3. Private methods/functions (lowercase)
18+
19+
## Examples:
20+
21+
### Service with methods:
22+
23+
```go
24+
type UserService struct {
25+
repository *UserRepository
26+
}
27+
28+
// Public methods first
29+
func (s *UserService) CreateUser(user *User) error {
30+
if err := s.validateUser(user); err != nil {
31+
return err
32+
}
33+
return s.repository.Save(user)
34+
}
35+
36+
func (s *UserService) GetUser(id uuid.UUID) (*User, error) {
37+
return s.repository.FindByID(id)
38+
}
39+
40+
// Private methods at the bottom
41+
func (s *UserService) validateUser(user *User) error {
42+
if user.Name == "" {
43+
return errors.New("name is required")
44+
}
45+
return nil
46+
}
47+
```
48+
49+
### Package-level functions:
50+
51+
```go
52+
package utils
53+
54+
// Public functions first
55+
func ProcessData(data []byte) (Result, error) {
56+
cleaned := sanitizeInput(data)
57+
return parseData(cleaned)
58+
}
59+
60+
func ValidateInput(input string) bool {
61+
return isValidFormat(input) && checkLength(input)
62+
}
63+
64+
// Private functions at the bottom
65+
func sanitizeInput(data []byte) []byte {
66+
// implementation
67+
}
68+
69+
func parseData(data []byte) (Result, error) {
70+
// implementation
71+
}
72+
73+
func isValidFormat(input string) bool {
74+
// implementation
75+
}
76+
77+
func checkLength(input string) bool {
78+
// implementation
79+
}
80+
```
81+
82+
### Test files:
83+
84+
```go
85+
package user_test
86+
87+
// Public test functions first
88+
func Test_CreateUser_ValidInput_UserCreated(t *testing.T) {
89+
user := createTestUser()
90+
result, err := service.CreateUser(user)
91+
92+
assert.NoError(t, err)
93+
assert.NotNil(t, result)
94+
}
95+
96+
func Test_GetUser_ExistingUser_ReturnsUser(t *testing.T) {
97+
user := createTestUser()
98+
// test implementation
99+
}
100+
101+
// Private helper functions at the bottom
102+
func createTestUser() *User {
103+
return &User{
104+
Name: "Test User",
105+
106+
}
107+
}
108+
109+
func setupTestDatabase() *Database {
110+
// setup implementation
111+
}
112+
```
113+
114+
### Controller example:
115+
116+
```go
117+
type ProjectController struct {
118+
service *ProjectService
119+
}
120+
121+
// Public HTTP handlers first
122+
func (c *ProjectController) CreateProject(ctx *gin.Context) {
123+
var request CreateProjectRequest
124+
if err := ctx.ShouldBindJSON(&request); err != nil {
125+
c.handleError(ctx, err)
126+
return
127+
}
128+
// handler logic
129+
}
130+
131+
func (c *ProjectController) GetProject(ctx *gin.Context) {
132+
projectID := c.extractProjectID(ctx)
133+
// handler logic
134+
}
135+
136+
// Private helper methods at the bottom
137+
func (c *ProjectController) handleError(ctx *gin.Context, err error) {
138+
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
139+
}
140+
141+
func (c *ProjectController) extractProjectID(ctx *gin.Context) uuid.UUID {
142+
return uuid.MustParse(ctx.Param("projectId"))
143+
}
144+
```
9145

10-
type SomeService struct {
11-
func PublicMethod(...) ...
146+
## Key Points:
12147

13-
func privateMethod(...) ...
14-
}
148+
- **Exported/Public** = starts with uppercase letter (CreateUser, GetProject)
149+
- **Unexported/Private** = starts with lowercase letter (validateUser, handleError)
150+
- This improves code readability by showing the public API first
151+
- Private helpers are implementation details, so they go at the bottom
152+
- Apply this rule consistently across ALL Go files in the project

backend/.cursor/rules/comments.mdc

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,45 @@
11
---
2-
description:
3-
globs:
2+
description:
3+
globs:
44
alwaysApply: true
55
---
6-
Do not write obvious comments.
7-
Write meaningful code, give meaningful names
6+
7+
## Comment Guidelines
8+
9+
1. **No obvious comments** - Don't state what the code already clearly shows
10+
2. **Functions and variables should have meaningful names** - Code should be self-documenting
11+
3. **Comments for unclear code only** - Only add comments when code logic isn't immediately clear
12+
13+
## Key Principles:
14+
15+
- **Code should tell a story** - Use descriptive variable and function names
16+
- **Comments explain WHY, not WHAT** - The code shows what happens, comments explain business logic or complex decisions
17+
- **Prefer refactoring over commenting** - If code needs explaining, consider making it clearer instead
18+
- **API documentation is required** - Swagger comments for all HTTP endpoints are mandatory
19+
- **Complex algorithms deserve comments** - Mathematical formulas, business rules, or non-obvious optimizations
20+
21+
Example of useless comment:
22+
23+
1.
24+
25+
```sql
26+
// Create projects table
27+
CREATE TABLE projects (
28+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
29+
name TEXT NOT NULL,
30+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
31+
```
32+
33+
2.
34+
35+
```go
36+
// Create test project
37+
project := CreateTestProject(projectName, user, router)
38+
```
39+
40+
3.
41+
42+
```go
43+
// CreateValidLogItems creates valid log items for testing
44+
func CreateValidLogItems(count int, uniqueID string) []logs_receiving.LogItemRequestDTO {
45+
```

backend/.cursor/rules/controllers-rule.mdc

Lines changed: 104 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -14,42 +14,114 @@ func (c *TasksController) GetAvailableTasks(ctx *gin.Context) ...
1414

1515
3. We document all routes with Swagger in the following format:
1616

17-
// SignIn
18-
// @Summary Authenticate a user
19-
// @Description Authenticate a user with email and password
20-
// @Tags users
17+
package audit_logs
18+
19+
import (
20+
"net/http"
21+
22+
user_models "logbull/internal/features/users/models"
23+
24+
"github.com/gin-gonic/gin"
25+
"github.com/google/uuid"
26+
)
27+
28+
type AuditLogController struct {
29+
auditLogService *AuditLogService
30+
}
31+
32+
func (c *AuditLogController) RegisterRoutes(router *gin.RouterGroup) {
33+
// All audit log endpoints require authentication (handled in main.go)
34+
auditRoutes := router.Group("/audit-logs")
35+
36+
auditRoutes.GET("/global", c.GetGlobalAuditLogs)
37+
auditRoutes.GET("/users/:userId", c.GetUserAuditLogs)
38+
}
39+
40+
// GetGlobalAuditLogs
41+
// @Summary Get global audit logs (ADMIN only)
42+
// @Description Retrieve all audit logs across the system
43+
// @Tags audit-logs
2144
// @Accept json
2245
// @Produce json
23-
// @Param request body SignInRequest true "User signin data"
24-
// @Success 200 {object} SignInResponse
25-
// @Failure 400
26-
// @Router /users/signin [post]
46+
// @Security BearerAuth
47+
// @Param limit query int false "Limit number of results" default(100)
48+
// @Param offset query int false "Offset for pagination" default(0)
49+
// @Param beforeDate query string false "Filter logs created before this date (RFC3339 format)" format(date-time)
50+
// @Success 200 {object} GetAuditLogsResponse
51+
// @Failure 401 {object} map[string]string
52+
// @Failure 403 {object} map[string]string
53+
// @Router /audit-logs/global [get]
54+
func (c *AuditLogController) GetGlobalAuditLogs(ctx *gin.Context) {
55+
user, isOk := ctx.MustGet("user").(*user_models.User)
56+
if !isOk {
57+
ctx.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid user type in context"})
58+
return
59+
}
2760

28-
Do not forget to write docs.
29-
You can avoid description if it is useless.
30-
Specify particular acceping \ producing models
61+
request := &GetAuditLogsRequest{}
62+
if err := ctx.ShouldBindQuery(request); err != nil {
63+
ctx.JSON(http.StatusBadRequest, gin.H{"error": "Invalid query parameters"})
64+
return
65+
}
3166

32-
4. All controllers should have RegisterRoutes method which receives
33-
RouterGroup (always put this routes on the top of file under controller definition)
67+
response, err := c.auditLogService.GetGlobalAuditLogs(user, request)
68+
if err != nil {
69+
if err.Error() == "only administrators can view global audit logs" {
70+
ctx.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
71+
return
72+
}
73+
ctx.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to retrieve audit logs"})
74+
return
75+
}
3476

35-
Example:
36-
37-
func (c *OrderController) RegisterRoutes(router *gin.RouterGroup) {
38-
router.POST("/bots/users/orders/generate", c.GenerateOrder)
39-
router.POST("/bots/users/orders/generate-by-admin", c.GenerateOrderByAdmin)
40-
router.GET("/bots/users/orders/mark-as-paid-by-admin", c.MarkOrderAsPaidByAdmin)
41-
router.GET("/bots/users/orders/payments-by-bot", c.GetOrderPaymentsByBot)
42-
router.GET("/bots/users/orders/payments-by-user", c.GetOrderPaymentsByUser)
43-
router.GET("/bots/users/orders/orders-by-user-for-admin", c.GetOrdersByUserForAdmin)
44-
router.POST("/bots/users/orders/orders-by-user-for-user", c.GetOrdersByUserForUser)
45-
router.POST("/bots/users/orders/order", c.GetOrder)
46-
router.POST("/bots/users/orders/cancel-subscription-by-user", c.CancelSubscriptionByUser)
47-
router.GET("/bots/users/orders/cancel-subscription-by-admin", c.CancelSubscriptionByAdmin)
48-
router.GET(
49-
"/bots/users/orders/cancel-subscriptions-by-payment-option",
50-
c.CancelSubscriptionsByPaymentOption,
51-
)
77+
ctx.JSON(http.StatusOK, response)
5278
}
5379

54-
5. Check that use use valid .Query("param") and .Param("param") methods.
55-
If route does not have param - use .Query("query")
80+
// GetUserAuditLogs
81+
// @Summary Get user audit logs
82+
// @Description Retrieve audit logs for a specific user
83+
// @Tags audit-logs
84+
// @Accept json
85+
// @Produce json
86+
// @Security BearerAuth
87+
// @Param userId path string true "User ID"
88+
// @Param limit query int false "Limit number of results" default(100)
89+
// @Param offset query int false "Offset for pagination" default(0)
90+
// @Param beforeDate query string false "Filter logs created before this date (RFC3339 format)" format(date-time)
91+
// @Success 200 {object} GetAuditLogsResponse
92+
// @Failure 400 {object} map[string]string
93+
// @Failure 401 {object} map[string]string
94+
// @Failure 403 {object} map[string]string
95+
// @Router /audit-logs/users/{userId} [get]
96+
func (c *AuditLogController) GetUserAuditLogs(ctx *gin.Context) {
97+
user, isOk := ctx.MustGet("user").(*user_models.User)
98+
if !isOk {
99+
ctx.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid user type in context"})
100+
return
101+
}
102+
103+
userIDStr := ctx.Param("userId")
104+
targetUserID, err := uuid.Parse(userIDStr)
105+
if err != nil {
106+
ctx.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user ID"})
107+
return
108+
}
109+
110+
request := &GetAuditLogsRequest{}
111+
if err := ctx.ShouldBindQuery(request); err != nil {
112+
ctx.JSON(http.StatusBadRequest, gin.H{"error": "Invalid query parameters"})
113+
return
114+
}
115+
116+
response, err := c.auditLogService.GetUserAuditLogs(targetUserID, user, request)
117+
if err != nil {
118+
if err.Error() == "insufficient permissions to view user audit logs" {
119+
ctx.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
120+
return
121+
}
122+
ctx.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to retrieve audit logs"})
123+
return
124+
}
125+
126+
ctx.JSON(http.StatusOK, response)
127+
}

0 commit comments

Comments
 (0)