- 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.
147 lines
4.3 KiB
Go
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
|
|
}
|