Skip to content

Commit 431e986

Browse files
FEATURE (workspaces): Add workspaces with users management and global Postgresus settings
1 parent de1fd4c commit 431e986

File tree

227 files changed

+19455
-1528
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

227 files changed

+19455
-1528
lines changed

.github/workflows/ci-release.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,9 @@ jobs:
138138
TEST_MINIO_CONSOLE_PORT=9001
139139
# testing NAS
140140
TEST_NAS_PORT=7006
141+
# testing Telegram
142+
TEST_TELEGRAM_BOT_TOKEN=${{ secrets.TEST_TELEGRAM_BOT_TOKEN }}
143+
TEST_TELEGRAM_CHAT_ID=${{ secrets.TEST_TELEGRAM_CHAT_ID }}
141144
EOF
142145
143146
- name: Start test containers

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@
187187
same "license" line as the copyright notice for easier
188188
identification within third-party archives.
189189

190-
Copyright 2025 LogBull
190+
Copyright 2025 Postgresus
191191

192192
Licensed under the Apache License, Version 2.0 (the "License");
193193
you may not use this file except in compliance with the License.

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,11 @@ docker compose up -d
154154
If you need to reset the admin password, you can use the built-in password reset command:
155155

156156
```bash
157-
docker exec -it postgresus ./main --new-password="YourNewSecurePassword123"
157+
docker exec -it postgresus ./main --new-password="YourNewSecurePassword123" --email="admin"
158158
```
159159

160+
Replace `admin` with the actual email address of the user whose password you want to reset.
161+
160162
---
161163

162164
## 📝 License
Lines changed: 72 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
---
2-
description:
3-
globs:
2+
description:
3+
globs:
44
alwaysApply: true
55
---
6+
67
1. When we write controller:
8+
79
- we combine all routes to single controller
810
- names them as .WhatWeDo (not "handlers") concept
911

10-
2. We use gin and *gin.Context for all routes.
11-
Example:
12+
2. We use gin and \*gin.Context for all routes.
13+
Example:
1214

1315
func (c *TasksController) GetAvailableTasks(ctx *gin.Context) ...
1416

@@ -17,24 +19,26 @@ func (c *TasksController) GetAvailableTasks(ctx *gin.Context) ...
1719
package audit_logs
1820

1921
import (
20-
"net/http"
22+
"net/http"
23+
24+
user_models "postgresus/internal/features/users/models"
2125

22-
user_models "logbull/internal/features/users/models"
26+
"github.com/gin-gonic/gin"
27+
"github.com/google/uuid"
2328

24-
"github.com/gin-gonic/gin"
25-
"github.com/google/uuid"
2629
)
2730

2831
type AuditLogController struct {
29-
auditLogService *AuditLogService
32+
auditLogService \*AuditLogService
3033
}
3134

3235
func (c *AuditLogController) RegisterRoutes(router *gin.RouterGroup) {
33-
// All audit log endpoints require authentication (handled in main.go)
34-
auditRoutes := router.Group("/audit-logs")
36+
// All audit log endpoints require authentication (handled in main.go)
37+
auditRoutes := router.Group("/audit-logs")
38+
39+
auditRoutes.GET("/global", c.GetGlobalAuditLogs)
40+
auditRoutes.GET("/users/:userId", c.GetUserAuditLogs)
3541

36-
auditRoutes.GET("/global", c.GetGlobalAuditLogs)
37-
auditRoutes.GET("/users/:userId", c.GetUserAuditLogs)
3842
}
3943

4044
// GetGlobalAuditLogs
@@ -52,29 +56,30 @@ func (c *AuditLogController) RegisterRoutes(router *gin.RouterGroup) {
5256
// @Failure 403 {object} map[string]string
5357
// @Router /audit-logs/global [get]
5458
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-
}
60-
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-
}
66-
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-
}
76-
77-
ctx.JSON(http.StatusOK, response)
59+
user, isOk := ctx.MustGet("user").(\*user_models.User)
60+
if !isOk {
61+
ctx.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid user type in context"})
62+
return
63+
}
64+
65+
request := &GetAuditLogsRequest{}
66+
if err := ctx.ShouldBindQuery(request); err != nil {
67+
ctx.JSON(http.StatusBadRequest, gin.H{"error": "Invalid query parameters"})
68+
return
69+
}
70+
71+
response, err := c.auditLogService.GetGlobalAuditLogs(user, request)
72+
if err != nil {
73+
if err.Error() == "only administrators can view global audit logs" {
74+
ctx.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
75+
return
76+
}
77+
ctx.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to retrieve audit logs"})
78+
return
79+
}
80+
81+
ctx.JSON(http.StatusOK, response)
82+
7883
}
7984

8085
// GetUserAuditLogs
@@ -94,34 +99,35 @@ func (c *AuditLogController) GetGlobalAuditLogs(ctx *gin.Context) {
9499
// @Failure 403 {object} map[string]string
95100
// @Router /audit-logs/users/{userId} [get]
96101
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)
102+
user, isOk := ctx.MustGet("user").(\*user_models.User)
103+
if !isOk {
104+
ctx.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid user type in context"})
105+
return
106+
}
107+
108+
userIDStr := ctx.Param("userId")
109+
targetUserID, err := uuid.Parse(userIDStr)
110+
if err != nil {
111+
ctx.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user ID"})
112+
return
113+
}
114+
115+
request := &GetAuditLogsRequest{}
116+
if err := ctx.ShouldBindQuery(request); err != nil {
117+
ctx.JSON(http.StatusBadRequest, gin.H{"error": "Invalid query parameters"})
118+
return
119+
}
120+
121+
response, err := c.auditLogService.GetUserAuditLogs(targetUserID, user, request)
122+
if err != nil {
123+
if err.Error() == "insufficient permissions to view user audit logs" {
124+
ctx.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
125+
return
126+
}
127+
ctx.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to retrieve audit logs"})
128+
return
129+
}
130+
131+
ctx.JSON(http.StatusOK, response)
132+
127133
}

backend/.cursor/rules/crud.mdc

Lines changed: 46 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
---
22
alwaysApply: false
33
---
4+
45
This is example of CRUD:
56

67
------ backend/internal/features/audit_logs/controller.go ------
7-
``````
8+
9+
```
810
package audit_logs
911

1012
import (
1113
"net/http"
1214

13-
user_models "logbull/internal/features/users/models"
15+
user_models "postgresus/internal/features/users/models"
1416

1517
"github.com/gin-gonic/gin"
1618
"github.com/google/uuid"
@@ -117,9 +119,11 @@ func (c *AuditLogController) GetUserAuditLogs(ctx *gin.Context) {
117119
ctx.JSON(http.StatusOK, response)
118120
}
119121

120-
``````
122+
```
123+
121124
------ backend/internal/features/audit_logs/controller_test.go ------
122-
``````
125+
126+
```
123127
package audit_logs
124128

125129
import (
@@ -128,12 +132,12 @@ import (
128132
"testing"
129133
"time"
130134

131-
user_enums "logbull/internal/features/users/enums"
132-
users_middleware "logbull/internal/features/users/middleware"
133-
users_services "logbull/internal/features/users/services"
134-
users_testing "logbull/internal/features/users/testing"
135-
"logbull/internal/storage"
136-
test_utils "logbull/internal/util/testing"
135+
user_enums "postgresus/internal/features/users/enums"
136+
users_middleware "postgresus/internal/features/users/middleware"
137+
users_services "postgresus/internal/features/users/services"
138+
users_testing "postgresus/internal/features/users/testing"
139+
"postgresus/internal/storage"
140+
test_utils "postgresus/internal/util/testing"
137141

138142
"github.com/gin-gonic/gin"
139143
"github.com/google/uuid"
@@ -256,14 +260,16 @@ func createRouter() *gin.Engine {
256260
return router
257261
}
258262

259-
``````
263+
```
264+
260265
------ backend/internal/features/audit_logs/di.go ------
261-
``````
266+
267+
```
262268
package audit_logs
263269

264270
import (
265-
users_services "logbull/internal/features/users/services"
266-
"logbull/internal/util/logger"
271+
users_services "postgresus/internal/features/users/services"
272+
"postgresus/internal/util/logger"
267273
)
268274

269275
var auditLogRepository = &AuditLogRepository{}
@@ -289,9 +295,11 @@ func SetupDependencies() {
289295
users_services.GetManagementService().SetAuditLogWriter(auditLogService)
290296
}
291297

292-
``````
298+
```
299+
293300
------ backend/internal/features/audit_logs/dto.go ------
294-
``````
301+
302+
```
295303
package audit_logs
296304

297305
import "time"
@@ -309,9 +317,11 @@ type GetAuditLogsResponse struct {
309317
Offset int `json:"offset"`
310318
}
311319

312-
``````
320+
```
321+
313322
------ backend/internal/features/audit_logs/models.go ------
314-
``````
323+
324+
```
315325
package audit_logs
316326

317327
import (
@@ -332,13 +342,15 @@ func (AuditLog) TableName() string {
332342
return "audit_logs"
333343
}
334344

335-
``````
345+
```
346+
336347
------ backend/internal/features/audit_logs/repository.go ------
337-
``````
348+
349+
```
338350
package audit_logs
339351

340352
import (
341-
"logbull/internal/storage"
353+
"postgresus/internal/storage"
342354
"time"
343355

344356
"github.com/google/uuid"
@@ -429,18 +441,20 @@ func (r *AuditLogRepository) CountGlobal(beforeDate *time.Time) (int64, error) {
429441
return count, err
430442
}
431443

432-
``````
444+
```
445+
433446
------ backend/internal/features/audit_logs/service.go ------
434-
``````
447+
448+
```
435449
package audit_logs
436450

437451
import (
438452
"errors"
439453
"log/slog"
440454
"time"
441455

442-
user_enums "logbull/internal/features/users/enums"
443-
user_models "logbull/internal/features/users/models"
456+
user_enums "postgresus/internal/features/users/enums"
457+
user_models "postgresus/internal/features/users/models"
444458

445459
"github.com/google/uuid"
446460
)
@@ -560,17 +574,19 @@ func (s *AuditLogService) GetProjectAuditLogs(
560574
}, nil
561575
}
562576

563-
``````
577+
```
578+
564579
------ backend/internal/features/audit_logs/service_test.go ------
565-
``````
580+
581+
```
566582
package audit_logs
567583

568584
import (
569585
"testing"
570586
"time"
571587

572-
user_enums "logbull/internal/features/users/enums"
573-
users_testing "logbull/internal/features/users/testing"
588+
user_enums "postgresus/internal/features/users/enums"
589+
users_testing "postgresus/internal/features/users/testing"
574590

575591
"github.com/google/uuid"
576592
"github.com/stretchr/testify/assert"
@@ -652,4 +668,4 @@ func createTimedLog(db *gorm.DB, userID *uuid.UUID, message string, createdAt ti
652668
db.Create(log)
653669
}
654670

655-
``````
671+
```

backend/.env.development.example

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,7 @@ TEST_POSTGRES_18_PORT=5006
2727
TEST_MINIO_PORT=9000
2828
TEST_MINIO_CONSOLE_PORT=9001
2929
# testing NAS
30-
TEST_NAS_PORT=7006
30+
TEST_NAS_PORT=7006
31+
# testing Telegram
32+
TEST_TELEGRAM_BOT_TOKEN=
33+
TEST_TELEGRAM_CHAT_ID=

0 commit comments

Comments
 (0)