Skip to content

Commit 3303268

Browse files
authored
v0.1.0-alpha.11 (#270)
1 parent 7320c5c commit 3303268

Some content is hidden

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

51 files changed

+5380
-153
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ docs/.vitepress/cache
1212
.env.staging
1313
!api/.env.sample
1414
**.log
15+
.vscode

api/api/versions.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
{
44
"version": "v1",
55
"status": "active",
6-
"release_date": "2025-06-30T20:48:43.246107+05:30",
6+
"release_date": "2025-07-10T02:54:43.011943+05:30",
77
"end_of_life": "0001-01-01T00:00:00Z",
88
"changes": [
99
"Initial API version"

api/internal/features/auth/service/register.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ func (c *AuthService) Register(registrationRequest types.RegisterRequest, userTy
2929
return types.AuthResponse{}, types.ErrUserWithEmailAlreadyExists
3030
}
3131

32+
if dbUser, err := c.storage.FindUserByUsername(registrationRequest.Username); err == nil && dbUser.ID != uuid.Nil {
33+
c.logger.Log(logger.Error, types.ErrUserWithUsernameAlreadyExists.Error(), "")
34+
return types.AuthResponse{}, types.ErrUserWithUsernameAlreadyExists
35+
}
36+
3237
hashedPassword, err := utils.HashPassword(registrationRequest.Password)
3338
if err != nil {
3439
c.logger.Log(logger.Error, types.ErrFailedToHashPassword.Error(), err.Error())

api/internal/features/auth/storage/user.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ type UserStorage struct {
2222

2323
type AuthRepository interface {
2424
FindUserByEmail(email string) (*types.User, error)
25+
FindUserByUsername(username string) (*types.User, error)
2526
FindUserByID(id string) (*types.User, error)
2627
CreateUser(user *types.User) error
2728
UpdateUser(user *types.User) error
@@ -94,6 +95,47 @@ func (u *UserStorage) FindUserByEmail(email string) (*types.User, error) {
9495
return user, nil
9596
}
9697

98+
// FindUserByUsername finds a user by username in the database.
99+
//
100+
// The function returns an error if the user does not exist or if the query
101+
// fails.
102+
func (u *UserStorage) FindUserByUsername(username string) (*types.User, error) {
103+
user := &types.User{}
104+
err := u.getDB().NewSelect().
105+
Model(user).
106+
Where("username = ?", username).
107+
Relation("Organizations").
108+
Scan(u.Ctx)
109+
if err != nil {
110+
return nil, err
111+
}
112+
113+
err = u.getDB().NewSelect().
114+
Model(&user.OrganizationUsers).
115+
Where("user_id = ?", user.ID).
116+
Relation("Role").
117+
Relation("Organization").
118+
Scan(u.Ctx)
119+
if err != nil {
120+
return nil, err
121+
}
122+
123+
for i, orgUser := range user.OrganizationUsers {
124+
if orgUser.Role != nil {
125+
err = u.getDB().NewSelect().
126+
Model(&user.OrganizationUsers[i].Role.Permissions).
127+
Join("JOIN role_permissions AS rp ON rp.permission_id = p.id").
128+
Where("rp.role_id = ?", orgUser.Role.ID).
129+
Scan(u.Ctx)
130+
if err != nil {
131+
return nil, err
132+
}
133+
}
134+
}
135+
136+
return user, nil
137+
}
138+
97139
// FindUserByID finds a user by id in the database.
98140
//
99141
// The function returns an error if the user does not exist or if the query

api/internal/features/auth/types/auth.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ var (
8585
ErrFailedToDecodeRequest = errors.New("failed to decode request body")
8686
ErrMissingRequiredFields = errors.New("missing required fields")
8787
ErrUserWithEmailAlreadyExists = errors.New("user with email already exists")
88+
ErrUserWithUsernameAlreadyExists = errors.New("user with username already exists")
8889
ErrFailedToRegisterUser = errors.New("failed to register user")
8990
ErrFailedToHashPassword = errors.New("failed to hash password")
9091
ErrFailedToCreateToken = errors.New("failed to create token")

api/internal/features/domain/controller/create_domain.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,21 @@ func (c *DomainsController) CreateDomain(f fuego.ContextWithBody[types.CreateDom
4242

4343
if err != nil {
4444
c.logger.Log(logger.Error, err.Error(), "")
45+
46+
if isInvalidDomainError(err) {
47+
return nil, fuego.HTTPError{
48+
Err: err,
49+
Status: http.StatusBadRequest,
50+
}
51+
}
52+
53+
if err == types.ErrDomainAlreadyExists {
54+
return nil, fuego.HTTPError{
55+
Err: err,
56+
Status: http.StatusConflict,
57+
}
58+
}
59+
4560
return nil, fuego.HTTPError{
4661
Err: err,
4762
Status: http.StatusInternalServerError,

api/internal/features/domain/controller/delete_domain.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,21 @@ func (c *DomainsController) DeleteDomain(f fuego.ContextWithBody[types.DeleteDom
3131
err = c.service.DeleteDomain(domainRequest.ID)
3232
if err != nil {
3333
c.logger.Log(logger.Error, err.Error(), "")
34+
35+
if isInvalidDomainError(err) {
36+
return nil, fuego.HTTPError{
37+
Err: err,
38+
Status: http.StatusBadRequest,
39+
}
40+
}
41+
42+
if err == types.ErrDomainNotFound {
43+
return nil, fuego.HTTPError{
44+
Err: err,
45+
Status: http.StatusNotFound,
46+
}
47+
}
48+
3449
return nil, fuego.HTTPError{
3550
Err: err,
3651
Status: http.StatusInternalServerError,
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package controller
2+
3+
import "github.com/raghavyuva/nixopus-api/internal/features/domain/types"
4+
5+
func isInvalidDomainError(err error) bool {
6+
switch err {
7+
case types.ErrInvalidDomainID,
8+
types.ErrMissingDomainID,
9+
types.ErrDomainNameInvalid,
10+
types.ErrDomainNameTooLong,
11+
types.ErrDomainNameTooShort,
12+
types.ErrMissingDomainName:
13+
return true
14+
default:
15+
return false
16+
}
17+
}
18+
19+
func isPermissionError(err error) bool {
20+
switch err {
21+
case types.ErrUserDoesNotBelongToOrganization,
22+
types.ErrPermissionDenied:
23+
return true
24+
default:
25+
return false
26+
}
27+
}

api/internal/features/domain/controller/get_domains.go

Lines changed: 64 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"time"
88

99
"github.com/go-fuego/fuego"
10+
"github.com/google/uuid"
1011
"github.com/raghavyuva/nixopus-api/internal/features/domain/types"
1112
"github.com/raghavyuva/nixopus-api/internal/features/logger"
1213
"github.com/raghavyuva/nixopus-api/internal/utils"
@@ -15,24 +16,46 @@ import (
1516
)
1617

1718
func (c *DomainsController) GetDomains(f fuego.ContextNoBody) (*shared_types.Response, error) {
18-
organization_id := f.QueryParam("id")
19-
2019
w, r := f.Response(), f.Request()
2120

22-
user := utils.GetUser(w, r)
21+
organization_id := utils.GetOrganizationID(r)
22+
if organization_id == uuid.Nil {
23+
c.logger.Log(logger.Error, "invalid organization id", "")
24+
return nil, fuego.HTTPError{
25+
Err: types.ErrMissingID,
26+
Status: http.StatusBadRequest,
27+
}
28+
}
2329

30+
user := utils.GetUser(w, r)
2431
if user == nil {
32+
c.logger.Log(logger.Error, "unauthorized user", "")
2533
return nil, fuego.HTTPError{
26-
Err: nil,
34+
Err: types.ErrAccessDenied,
2735
Status: http.StatusUnauthorized,
2836
}
2937
}
3038

3139
c.logger.Log(logger.Info, "fetching domains", fmt.Sprintf("organization_id: %s", organization_id))
3240

33-
domains, err := c.service.GetDomains(organization_id, user.ID)
41+
domains, err := c.service.GetDomains(organization_id.String(), user.ID)
3442
if err != nil {
3543
c.logger.Log(logger.Error, err.Error(), "")
44+
45+
if isPermissionError(err) {
46+
return nil, fuego.HTTPError{
47+
Err: err,
48+
Status: http.StatusForbidden,
49+
}
50+
}
51+
52+
if err == types.ErrDomainNotFound {
53+
return nil, fuego.HTTPError{
54+
Err: err,
55+
Status: http.StatusNotFound,
56+
}
57+
}
58+
3659
return nil, fuego.HTTPError{
3760
Err: err,
3861
Status: http.StatusInternalServerError,
@@ -49,22 +72,53 @@ func (c *DomainsController) GetDomains(f fuego.ContextNoBody) (*shared_types.Res
4972
func (c *DomainsController) GenerateRandomSubDomain(f fuego.ContextNoBody) (*shared_types.Response, error) {
5073
w, r := f.Response(), f.Request()
5174

52-
organization_id := f.QueryParam("id")
75+
organization_id := utils.GetOrganizationID(r)
76+
if organization_id == uuid.Nil {
77+
c.logger.Log(logger.Error, "invalid organization id", "")
78+
return nil, fuego.HTTPError{
79+
Err: types.ErrMissingID,
80+
Status: http.StatusBadRequest,
81+
}
82+
}
83+
84+
user := utils.GetUser(w, r)
85+
if user == nil {
86+
c.logger.Log(logger.Error, "unauthorized user", "")
87+
return nil, fuego.HTTPError{
88+
Err: types.ErrAccessDenied,
89+
Status: http.StatusUnauthorized,
90+
}
91+
}
5392

54-
domains, err := c.service.GetDomains(organization_id, utils.GetUser(w, r).ID)
93+
domains, err := c.service.GetDomains(organization_id.String(), user.ID)
5594
if err != nil {
5695
c.logger.Log(logger.Error, err.Error(), "")
96+
97+
if isPermissionError(err) {
98+
return nil, fuego.HTTPError{
99+
Err: err,
100+
Status: http.StatusForbidden,
101+
}
102+
}
103+
104+
if err == types.ErrDomainNotFound {
105+
return nil, fuego.HTTPError{
106+
Err: err,
107+
Status: http.StatusNotFound,
108+
}
109+
}
110+
57111
return nil, fuego.HTTPError{
58112
Err: err,
59113
Status: http.StatusInternalServerError,
60114
}
61115
}
62116

63117
if len(domains) == 0 {
64-
c.logger.Log(logger.Error, "no domains available", "")
118+
c.logger.Log(logger.Error, "no domains available for subdomain generation", "")
65119
return nil, fuego.HTTPError{
66-
Err: nil,
67-
Status: http.StatusBadRequest,
120+
Err: types.ErrDomainNotFound,
121+
Status: http.StatusNotFound,
68122
}
69123
}
70124

api/internal/features/domain/controller/update_domain.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,21 @@ func (c *DomainsController) UpdateDomain(f fuego.ContextWithBody[types.UpdateDom
4141
updated, err := c.service.UpdateDomain(domainRequest.Name, user.ID.String(), domainRequest.ID)
4242
if err != nil {
4343
c.logger.Log(logger.Error, err.Error(), "")
44+
45+
if isInvalidDomainError(err) {
46+
return nil, fuego.HTTPError{
47+
Err: err,
48+
Status: http.StatusBadRequest,
49+
}
50+
}
51+
52+
if err == types.ErrDomainNotFound {
53+
return nil, fuego.HTTPError{
54+
Err: err,
55+
Status: http.StatusNotFound,
56+
}
57+
}
58+
4459
return nil, fuego.HTTPError{
4560
Err: err,
4661
Status: http.StatusInternalServerError,

0 commit comments

Comments
 (0)