mirror of
https://github.com/GlueOps/autoglue.git
synced 2026-02-14 13:20:05 +01:00
Orgs, Members, SSH and Admin page
This commit is contained in:
@@ -4,11 +4,14 @@ import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/glueops/autoglue/internal/db"
|
||||
"github.com/glueops/autoglue/internal/db/models"
|
||||
"github.com/glueops/autoglue/internal/middleware"
|
||||
"github.com/glueops/autoglue/internal/response"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// CreateOrganization godoc
|
||||
@@ -88,3 +91,178 @@ func ListOrganizations(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
_ = response.JSON(w, http.StatusOK, orgs)
|
||||
}
|
||||
|
||||
// InviteMember godoc
|
||||
// @Summary Invite user to organization
|
||||
// @Tags organizations
|
||||
// @Accept json
|
||||
// @Produce plain
|
||||
// @Param body body InviteInput true "Invite input"
|
||||
// @Success 201 {string} string "invited"
|
||||
// @Failure 403 {string} string "forbidden"
|
||||
// @Failure 400 {string} string "bad request"
|
||||
// @Router /api/v1/orgs/invite [post]
|
||||
// @Param X-Org-ID header string true "Organization context"
|
||||
// @Security BearerAuth
|
||||
func InviteMember(w http.ResponseWriter, r *http.Request) {
|
||||
auth := middleware.GetAuthContext(r)
|
||||
if auth == nil || auth.OrgRole != "admin" || auth.OrganizationID == uuid.Nil {
|
||||
http.Error(w, "unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
var input InviteInput
|
||||
json.NewDecoder(r.Body).Decode(&input)
|
||||
|
||||
var user models.User
|
||||
err := db.DB.Where("email = ?", input.Email).First(&user).Error
|
||||
if err != nil {
|
||||
http.Error(w, "user not found", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
invite := models.Invitation{
|
||||
OrganizationID: auth.OrganizationID,
|
||||
Email: input.Email,
|
||||
Role: input.Role,
|
||||
Status: "pending",
|
||||
ExpiresAt: time.Now().Add(48 * time.Hour),
|
||||
InviterID: auth.UserID,
|
||||
}
|
||||
|
||||
if err := db.DB.Create(&invite).Error; err != nil {
|
||||
http.Error(w, "failed to invite", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
_ = response.JSON(w, http.StatusCreated, invite)
|
||||
}
|
||||
|
||||
// ListMembers lists all members of the authenticated org
|
||||
// @Summary List organization members
|
||||
// @Description Returns a list of all members in the current organization
|
||||
// @Tags organizations
|
||||
// @Security BearerAuth
|
||||
// @Produce json
|
||||
// @Success 200 {array} models.Member
|
||||
// @Failure 401 {string} string "unauthorized"
|
||||
// @Router /api/v1/orgs/members [get]
|
||||
// @Param X-Org-ID header string true "Organization context"
|
||||
func ListMembers(w http.ResponseWriter, r *http.Request) {
|
||||
authCtx := middleware.GetAuthContext(r)
|
||||
if authCtx == nil || authCtx.OrganizationID == uuid.Nil {
|
||||
http.Error(w, "unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
var members []models.Member
|
||||
if err := db.DB.Preload("User").Preload("Organization").Where("organization_id = ?", authCtx.OrganizationID).Find(&members).Error; err != nil {
|
||||
http.Error(w, "failed to fetch members", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
_ = response.JSON(w, http.StatusOK, members)
|
||||
}
|
||||
|
||||
// DeleteMember godoc
|
||||
// @Summary Remove member from organization
|
||||
// @Tags organizations
|
||||
// @Param userId path string true "User ID"
|
||||
// @Success 204 {string} string "deleted"
|
||||
// @Failure 403 {string} string "forbidden"
|
||||
// @Router /api/v1/orgs/members/{userId} [delete]
|
||||
// @Security BearerAuth
|
||||
func DeleteMember(w http.ResponseWriter, r *http.Request) {
|
||||
auth := middleware.GetAuthContext(r)
|
||||
if auth == nil || auth.OrgRole != "admin" {
|
||||
http.Error(w, "forbidden", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
userId := chi.URLParam(r, "userId")
|
||||
|
||||
if err := db.DB.Where("user_id = ? AND organization_id = ?", userId, auth.OrganizationID).Delete(&models.Member{}).Error; err != nil {
|
||||
http.Error(w, "failed to delete", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// UpdateOrganization godoc
|
||||
// @Summary Update organization metadata
|
||||
// @Tags organizations
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param orgId path string true "Org ID"
|
||||
// @Param body body OrgInput true "Organization data"
|
||||
// @Success 200 {object} models.Organization
|
||||
// @Failure 403 {string} string "forbidden"
|
||||
// @Router /api/v1/orgs/{orgId} [patch]
|
||||
// @Security BearerAuth
|
||||
func UpdateOrganization(w http.ResponseWriter, r *http.Request) {
|
||||
auth := middleware.GetAuthContext(r)
|
||||
if auth == nil || auth.OrgRole != "admin" {
|
||||
http.Error(w, "forbidden", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
orgId := chi.URLParam(r, "orgId")
|
||||
|
||||
var input OrgInput
|
||||
json.NewDecoder(r.Body).Decode(&input)
|
||||
|
||||
var org models.Organization
|
||||
db.DB.First(&org, "id = ?", orgId)
|
||||
|
||||
org.Name = input.Name
|
||||
org.Slug = input.Slug
|
||||
db.DB.Save(&org)
|
||||
|
||||
_ = response.JSON(w, http.StatusOK, org)
|
||||
}
|
||||
|
||||
// DeleteOrganization godoc
|
||||
// @Summary Delete organization
|
||||
// @Tags organizations
|
||||
// @Param orgId path string true "Organization ID"
|
||||
// @Success 204 {string} string "deleted"
|
||||
// @Failure 403 {string} string "forbidden"
|
||||
// @Router /api/v1/orgs/{orgId} [delete]
|
||||
// @Security BearerAuth
|
||||
func DeleteOrganization(w http.ResponseWriter, r *http.Request) {
|
||||
auth := middleware.GetAuthContext(r)
|
||||
if auth == nil {
|
||||
http.Error(w, "forbidden", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
orgId := chi.URLParam(r, "orgId")
|
||||
orgUUID, err := uuid.Parse(orgId)
|
||||
if err != nil {
|
||||
http.Error(w, "invalid organization id", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var member models.Member
|
||||
if err := db.DB.
|
||||
Where("user_id = ? AND organization_id = ?", auth.UserID, orgUUID).
|
||||
First(&member).Error; err != nil {
|
||||
http.Error(w, "forbidden", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
if member.Role != "admin" {
|
||||
http.Error(w, "forbidden", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
if err := db.DB.Where("organization_id = ?", orgUUID).Delete(&models.Member{}).Error; err != nil {
|
||||
http.Error(w, "failed to delete members", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if err := db.DB.Delete(&models.Organization{}, "id = ?", orgUUID).Error; err != nil {
|
||||
http.Error(w, "failed to delete org", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
response.NoContent(w)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user