ultisuite-backend/internal/api/mail/handlers_api_tokens.go
R3D347HR4Y 1d063237b9
Some checks are pending
CI / Go tests (push) Waiting to run
CI / Integration tests (push) Waiting to run
CI / DB migrations (push) Waiting to run
feat(transcription): integrate Faster Whisper for Jitsi transcriptions
- Added support for Faster Whisper transcription via Jigasi and Skynet.
- Updated .env.example to include new environment variables for transcription settings.
- Enhanced Jitsi Docker Compose configuration to include Skynet and Jigasi services.
- Introduced new API endpoints for managing organizational folders in the drive service.
- Updated Nextcloud initialization script to enable external file mounting.
- Improved error handling and response structures in the drive API.
- Added new properties for organization settings related to transcription and agenda management.
2026-06-12 19:10:18 +02:00

147 lines
4.3 KiB
Go

package mail
import (
"encoding/json"
"net/http"
"strings"
"time"
"github.com/go-chi/chi/v5"
"github.com/jackc/pgx/v5/pgxpool"
"github.com/ultisuite/ulti-backend/internal/api/apiresponse"
"github.com/ultisuite/ulti-backend/internal/api/apivalidate"
"github.com/ultisuite/ulti-backend/internal/api/middleware"
"github.com/ultisuite/ulti-backend/internal/apitokens"
)
type createApiTokenRequest struct {
Name string `json:"name"`
Permissions []apitokens.PermissionGrant `json:"permissions"`
MailScope apitokens.MailScope `json:"mail_scope"`
DriveScope apitokens.DriveScope `json:"drive_scope"`
AgendaScope apitokens.AgendaScope `json:"agenda_scope"`
ExpiresAt *time.Time `json:"expires_at,omitempty"`
}
func (h *Handler) ListApiTokens(w http.ResponseWriter, r *http.Request) {
claims := middleware.ClaimsFromContext(r.Context())
db := h.db()
if db == nil {
apiresponse.WriteError(w, r, http.StatusInternalServerError, apiresponse.CodeInternal, "database unavailable", nil)
return
}
tokens, err := apitokens.List(r.Context(), db, claims.Sub)
if err != nil {
apiresponse.WriteError(w, r, http.StatusInternalServerError, apiresponse.CodeInternal, "failed to list api tokens", nil)
return
}
apiresponse.WriteJSON(w, http.StatusOK, map[string]any{"tokens": tokens})
}
func (h *Handler) CreateApiToken(w http.ResponseWriter, r *http.Request) {
claims := middleware.ClaimsFromContext(r.Context())
var req createApiTokenRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
apiresponse.WriteError(w, r, http.StatusBadRequest, apiresponse.CodeInvalidRequest, "invalid json", nil)
return
}
name := strings.TrimSpace(req.Name)
if name == "" {
apivalidate.WriteValidationError(w, r, apivalidate.NewValidationError(apivalidate.FieldDetail{
Field: "name", Message: "name is required",
}))
return
}
if !hasAnyPermission(req.Permissions) {
apivalidate.WriteValidationError(w, r, apivalidate.NewValidationError(apivalidate.FieldDetail{
Field: "permissions", Message: "at least one permission is required",
}))
return
}
db := h.db()
if db == nil {
apiresponse.WriteError(w, r, http.StatusInternalServerError, apiresponse.CodeInternal, "database unavailable", nil)
return
}
created, err := apitokens.Create(
r.Context(),
db,
claims.Sub,
name,
req.Permissions,
normalizeMailScope(req.MailScope),
normalizeDriveScope(req.DriveScope),
normalizeAgendaScope(req.AgendaScope),
req.ExpiresAt,
)
if err != nil {
apiresponse.WriteError(w, r, http.StatusInternalServerError, apiresponse.CodeInternal, "failed to create api token", nil)
return
}
apiresponse.WriteJSON(w, http.StatusCreated, created)
}
func (h *Handler) RevokeApiToken(w http.ResponseWriter, r *http.Request) {
claims := middleware.ClaimsFromContext(r.Context())
tokenID := chi.URLParam(r, "tokenID")
db := h.db()
if db == nil {
apiresponse.WriteError(w, r, http.StatusInternalServerError, apiresponse.CodeInternal, "database unavailable", nil)
return
}
if err := apitokens.Revoke(r.Context(), db, claims.Sub, tokenID); err != nil {
if err == apitokens.ErrNotFound {
apiresponse.WriteError(w, r, http.StatusNotFound, apiresponse.CodeNotFound, "api token not found", nil)
return
}
apiresponse.WriteError(w, r, http.StatusInternalServerError, apiresponse.CodeInternal, "failed to revoke api token", nil)
return
}
w.WriteHeader(http.StatusNoContent)
}
func hasAnyPermission(grants []apitokens.PermissionGrant) bool {
for _, g := range grants {
if g.Read || g.Write {
return true
}
}
return false
}
func normalizeMailScope(scope apitokens.MailScope) apitokens.MailScope {
if scope.AllAccounts || len(scope.AccountIDs) == 0 {
return apitokens.MailScope{AllAccounts: true, AccountIDs: nil}
}
return scope
}
func normalizeDriveScope(scope apitokens.DriveScope) apitokens.DriveScope {
if scope.AllFolders || len(scope.FolderPaths) == 0 {
return apitokens.DriveScope{AllFolders: true, FolderPaths: nil}
}
return scope
}
func normalizeAgendaScope(scope apitokens.AgendaScope) apitokens.AgendaScope {
if scope.AllCalendars || len(scope.CalendarIDs) == 0 {
return apitokens.AgendaScope{AllCalendars: true, CalendarIDs: nil}
}
return scope
}
func (h *Handler) db() *pgxpool.Pool {
if s, ok := h.svc.(*Service); ok {
return s.DB()
}
return nil
}