Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion api/internal/api/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -1400,7 +1400,7 @@ func (h *Handler) ListRepositories(c *gin.Context) {
ctx := c.Request.Context()

rows, err := h.db.DB().QueryContext(ctx, `
SELECT id, name, url, branch, COALESCE(type, 'template'), auth_type, last_sync, template_count, status, error_message, created_at, updated_at
SELECT id, COALESCE(name, ''), url, COALESCE(branch, 'main'), COALESCE(type, 'template'), COALESCE(auth_type, 'none'), last_sync, COALESCE(template_count, 0), COALESCE(status, 'pending'), error_message, created_at, updated_at
FROM repositories
ORDER BY name ASC
`)
Expand Down
2 changes: 1 addition & 1 deletion api/internal/db/groups.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ func (g *GroupDB) GetGroupByName(ctx context.Context, name string) (*models.Grou
func (g *GroupDB) ListGroups(ctx context.Context, groupType string, parentID *string) ([]*models.Group, error) {
query := `
SELECT g.id, g.name, COALESCE(g.display_name, '') as display_name,
COALESCE(g.description, '') as description, g.type, g.parent_id,
COALESCE(g.description, '') as description, COALESCE(g.type, 'team'), g.parent_id,
g.created_at, g.updated_at, COUNT(gm.user_id) as member_count
FROM groups g
LEFT JOIN group_memberships gm ON g.id = gm.group_id
Expand Down
2 changes: 1 addition & 1 deletion api/internal/db/users.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ func (u *UserDB) GetUserByEmail(ctx context.Context, email string) (*models.User
// ListUsers retrieves all users with optional filtering
func (u *UserDB) ListUsers(ctx context.Context, role, provider string, activeOnly bool) ([]*models.User, error) {
query := `
SELECT id, username, email, full_name, role, provider, active, created_at, updated_at, last_login
SELECT id, username, email, COALESCE(full_name, ''), COALESCE(role, 'user'), COALESCE(provider, 'local'), COALESCE(active, true), created_at, updated_at, last_login
FROM users
WHERE 1=1
`
Expand Down
18 changes: 18 additions & 0 deletions api/internal/handlers/users.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,24 @@ func (h *UserHandler) CreateUser(c *gin.Context) {
return
}

// Validate password for local auth users
if req.Provider == "" || req.Provider == "local" {
if req.Password == "" {
c.JSON(http.StatusBadRequest, ErrorResponse{
Error: "Invalid request",
Message: "Password is required for local authentication",
})
return
}
if len(req.Password) < 8 {
c.JSON(http.StatusBadRequest, ErrorResponse{
Error: "Invalid request",
Message: "Password must be at least 8 characters",
})
return
}
}

user, err := h.userDB.CreateUser(c.Request.Context(), &req)
if err != nil {
c.JSON(http.StatusInternalServerError, ErrorResponse{
Expand Down
6 changes: 3 additions & 3 deletions api/internal/models/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -414,9 +414,9 @@ type CreateUserRequest struct {
Username string `json:"username" binding:"required"`
Email string `json:"email" binding:"required,email"`
FullName string `json:"fullName" binding:"required"`
Password string `json:"password" binding:"required,min=8"` // Only for local auth
Role string `json:"role"` // user, admin, operator
Provider string `json:"provider"` // local, saml, oidc
Password string `json:"password"` // Required for local auth, validated in handler
Role string `json:"role"` // user, admin, operator
Provider string `json:"provider"` // local, saml, oidc
}

// UpdateUserRequest represents a request to update an existing user.
Expand Down
Loading