mirror of
https://github.com/warmbly/warmbly.git
synced 2026-09-05 08:01:23 +00:00
284 lines
7.2 KiB
Go
284 lines
7.2 KiB
Go
package handler
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
"github.com/warmbly/warmbly/internal/api/middleware"
|
|
"github.com/warmbly/warmbly/internal/errx"
|
|
"github.com/warmbly/warmbly/internal/models"
|
|
)
|
|
|
|
// Self-hosted side of the warmup pool link: Settings > Warmbly Cloud.
|
|
|
|
func (h *Handler) cloudLinkReady(c *gin.Context) bool {
|
|
if h.CloudLinkService == nil {
|
|
errx.JSON(c, errx.New(errx.NotImplemented, "cloud link is not enabled on this instance"))
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (h *Handler) CloudLinkStatus(c *gin.Context) {
|
|
if !h.cloudLinkReady(c) {
|
|
return
|
|
}
|
|
st, xerr := h.CloudLinkService.Status(c.Request.Context())
|
|
if xerr != nil {
|
|
errx.JSON(c, xerr)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, st)
|
|
}
|
|
|
|
func (h *Handler) CloudLinkConnectStart(c *gin.Context) {
|
|
if !h.cloudLinkReady(c) {
|
|
return
|
|
}
|
|
userID, err := middleware.GetUserUUID(c)
|
|
if err != nil {
|
|
errx.JSON(c, errx.ErrUnauthorized)
|
|
return
|
|
}
|
|
var req struct {
|
|
CloudURL string `json:"cloud_url"`
|
|
}
|
|
_ = c.ShouldBindJSON(&req)
|
|
p, xerr := h.CloudLinkService.StartConnect(c.Request.Context(), userID, req.CloudURL)
|
|
if xerr != nil {
|
|
errx.JSON(c, xerr)
|
|
return
|
|
}
|
|
c.JSON(http.StatusCreated, p)
|
|
}
|
|
|
|
func (h *Handler) CloudLinkConnectPoll(c *gin.Context) {
|
|
if !h.cloudLinkReady(c) {
|
|
return
|
|
}
|
|
userID, err := middleware.GetUserUUID(c)
|
|
if err != nil {
|
|
errx.JSON(c, errx.ErrUnauthorized)
|
|
return
|
|
}
|
|
res, xerr := h.CloudLinkService.PollConnect(c.Request.Context(), userID)
|
|
if xerr != nil {
|
|
errx.JSON(c, xerr)
|
|
return
|
|
}
|
|
if res.Status == models.PoolLinkCodeApproved && res.Link != nil {
|
|
h.auditOrg(c, models.AuditActionConnect, models.AuditEntityCloudLink, &res.Link.InstanceID, nil, map[string]string{"organization_name": res.Link.OrganizationName})
|
|
}
|
|
c.JSON(http.StatusOK, res)
|
|
}
|
|
|
|
func (h *Handler) CloudLinkDisconnect(c *gin.Context) {
|
|
if !h.cloudLinkReady(c) {
|
|
return
|
|
}
|
|
if xerr := h.CloudLinkService.Disconnect(c.Request.Context()); xerr != nil {
|
|
errx.JSON(c, xerr)
|
|
return
|
|
}
|
|
h.auditOrg(c, models.AuditActionDisconnect, models.AuditEntityCloudLink, nil, nil, nil)
|
|
c.Status(http.StatusNoContent)
|
|
}
|
|
|
|
func (h *Handler) CloudLinkMailboxes(c *gin.Context) {
|
|
if !h.cloudLinkReady(c) {
|
|
return
|
|
}
|
|
orgID := middleware.GetOrganizationID(c)
|
|
if orgID == nil {
|
|
errx.JSON(c, errx.ErrNoOrganization)
|
|
return
|
|
}
|
|
rows, xerr := h.CloudLinkService.ListMailboxes(c.Request.Context(), *orgID)
|
|
if xerr != nil {
|
|
errx.JSON(c, xerr)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"data": rows})
|
|
}
|
|
|
|
func cloudLinkAccountID(c *gin.Context) (uuid.UUID, *uuid.UUID, bool) {
|
|
orgID := middleware.GetOrganizationID(c)
|
|
if orgID == nil {
|
|
errx.JSON(c, errx.ErrNoOrganization)
|
|
return uuid.Nil, nil, false
|
|
}
|
|
id, err := uuid.Parse(c.Param("id"))
|
|
if err != nil {
|
|
errx.JSON(c, errx.ErrUuid)
|
|
return uuid.Nil, nil, false
|
|
}
|
|
return id, orgID, true
|
|
}
|
|
|
|
func (h *Handler) CloudLinkEnroll(c *gin.Context) {
|
|
if !h.cloudLinkReady(c) {
|
|
return
|
|
}
|
|
id, orgID, ok := cloudLinkAccountID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
row, xerr := h.CloudLinkService.Enroll(c.Request.Context(), *orgID, id)
|
|
if xerr != nil {
|
|
errx.JSON(c, xerr)
|
|
return
|
|
}
|
|
h.auditOrg(c, models.AuditActionStart, models.AuditEntityCloudLink, &id, nil, map[string]string{"email": row.Email})
|
|
c.JSON(http.StatusOK, row)
|
|
}
|
|
|
|
func (h *Handler) CloudLinkUnenroll(c *gin.Context) {
|
|
if !h.cloudLinkReady(c) {
|
|
return
|
|
}
|
|
id, orgID, ok := cloudLinkAccountID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
if xerr := h.CloudLinkService.Unenroll(c.Request.Context(), *orgID, id); xerr != nil {
|
|
errx.JSON(c, xerr)
|
|
return
|
|
}
|
|
h.auditOrg(c, models.AuditActionStop, models.AuditEntityCloudLink, &id, nil, nil)
|
|
c.Status(http.StatusNoContent)
|
|
}
|
|
|
|
func (h *Handler) CloudLinkPause(c *gin.Context) {
|
|
h.cloudLinkLifecycle(c, "pause", models.AuditActionPause)
|
|
}
|
|
func (h *Handler) CloudLinkResume(c *gin.Context) {
|
|
h.cloudLinkLifecycle(c, "resume", models.AuditActionResume)
|
|
}
|
|
|
|
func (h *Handler) cloudLinkLifecycle(c *gin.Context, action string, audit models.AuditAction) {
|
|
if !h.cloudLinkReady(c) {
|
|
return
|
|
}
|
|
id, orgID, ok := cloudLinkAccountID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
row, xerr := h.CloudLinkService.SetLifecycle(c.Request.Context(), *orgID, id, action)
|
|
if xerr != nil {
|
|
errx.JSON(c, xerr)
|
|
return
|
|
}
|
|
h.auditOrg(c, audit, models.AuditEntityCloudLink, &id, nil, nil)
|
|
c.JSON(http.StatusOK, row)
|
|
}
|
|
|
|
// Cloud-managed mailboxes: sign in through Warmbly Cloud, adopt workspace mailboxes.
|
|
|
|
func (h *Handler) CloudLinkOAuthStart(c *gin.Context) {
|
|
if !h.cloudLinkReady(c) {
|
|
return
|
|
}
|
|
orgID := middleware.GetOrganizationID(c)
|
|
userID, err := middleware.GetUserUUID(c)
|
|
if orgID == nil || err != nil {
|
|
errx.JSON(c, errx.ErrNoOrganization)
|
|
return
|
|
}
|
|
var req struct {
|
|
Provider models.InboxProvider `json:"provider"`
|
|
}
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
errx.JSON(c, errx.New(errx.BadRequest, "invalid request body"))
|
|
return
|
|
}
|
|
res, xerr := h.CloudLinkService.StartOAuth(c.Request.Context(), *orgID, userID, req.Provider)
|
|
if xerr != nil {
|
|
errx.JSON(c, xerr)
|
|
return
|
|
}
|
|
c.JSON(http.StatusCreated, res)
|
|
}
|
|
|
|
func (h *Handler) CloudLinkOAuthFinish(c *gin.Context) {
|
|
if !h.cloudLinkReady(c) {
|
|
return
|
|
}
|
|
orgID := middleware.GetOrganizationID(c)
|
|
userID, err := middleware.GetUserUUID(c)
|
|
if orgID == nil || err != nil {
|
|
errx.JSON(c, errx.ErrNoOrganization)
|
|
return
|
|
}
|
|
var req struct {
|
|
Session string `json:"session"`
|
|
}
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
errx.JSON(c, errx.New(errx.BadRequest, "invalid request body"))
|
|
return
|
|
}
|
|
acc, xerr := h.CloudLinkService.FinishOAuth(c.Request.Context(), *orgID, userID, req.Session)
|
|
if xerr != nil {
|
|
errx.JSON(c, xerr)
|
|
return
|
|
}
|
|
h.auditOrg(c, models.AuditActionConnect, models.AuditEntityEmailAccount, &acc.ID, nil, map[string]string{"email": acc.Email, "via": "warmbly_cloud"})
|
|
c.JSON(http.StatusCreated, acc)
|
|
}
|
|
|
|
func (h *Handler) CloudLinkWorkspaceMailboxes(c *gin.Context) {
|
|
if !h.cloudLinkReady(c) {
|
|
return
|
|
}
|
|
list, xerr := h.CloudLinkService.ListWorkspaceMailboxes(c.Request.Context())
|
|
if xerr != nil {
|
|
errx.JSON(c, xerr)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"data": list})
|
|
}
|
|
|
|
func (h *Handler) CloudLinkAdopt(c *gin.Context) {
|
|
if !h.cloudLinkReady(c) {
|
|
return
|
|
}
|
|
id, orgID, ok := cloudLinkAccountID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
userID, err := middleware.GetUserUUID(c)
|
|
if err != nil {
|
|
errx.JSON(c, errx.ErrUnauthorized)
|
|
return
|
|
}
|
|
acc, xerr := h.CloudLinkService.Adopt(c.Request.Context(), *orgID, userID, id)
|
|
if xerr != nil {
|
|
errx.JSON(c, xerr)
|
|
return
|
|
}
|
|
h.auditOrg(c, models.AuditActionConnect, models.AuditEntityEmailAccount, &acc.ID, nil, map[string]string{"email": acc.Email, "via": "warmbly_cloud"})
|
|
c.JSON(http.StatusCreated, acc)
|
|
}
|
|
|
|
// InternalCloudLinkToken is the worker's credential for a managed mailbox.
|
|
//
|
|
// GET /api/v1/internal/cloud-link/token/:id -> 200 {"access_token","expires_at"} | 4xx with the cloud's reason
|
|
func (h *Handler) InternalCloudLinkToken(c *gin.Context) {
|
|
if h.CloudLinkService == nil {
|
|
errx.JSON(c, errx.ErrNotFound)
|
|
return
|
|
}
|
|
id, err := uuid.Parse(c.Param("id"))
|
|
if err != nil {
|
|
errx.JSON(c, errx.ErrUuid)
|
|
return
|
|
}
|
|
tok, xerr := h.CloudLinkService.AccessToken(c.Request.Context(), id)
|
|
if xerr != nil {
|
|
errx.JSON(c, xerr)
|
|
return
|
|
}
|
|
c.Header("Cache-Control", "no-store")
|
|
c.JSON(http.StatusOK, tok)
|
|
}
|