fix: persist options when uploading documents & dynamic config OnlyAdminCanCreate

Reader options (readMode, allowDownload, requireFullRead, verifyChecksum)
were not being saved during document upload.

SimpleAuthorizer now reads the setting dynamically from ConfigService
instead of using a static value set at startup. This allows admins to
toggle document creation permissions via the settings UI without
requiring a server restart.

Fixes #14
Fixes #15
This commit is contained in:
Benjamin
2026-01-22 12:50:47 +01:00
parent 92f97eff43
commit bff75aafbe
6 changed files with 70 additions and 14 deletions

View File

@@ -55,6 +55,12 @@ type CreateDocumentRequest struct {
Title string `json:"title"`
CreatedBy string `json:"created_by,omitempty"`
// Reader options
ReadMode string `json:"read_mode,omitempty"`
AllowDownload *bool `json:"allow_download,omitempty"`
RequireFullRead *bool `json:"require_full_read,omitempty"`
VerifyChecksum *bool `json:"verify_checksum,omitempty"`
// Storage fields for uploaded files
StorageKey string `json:"storage_key,omitempty"`
StorageProvider string `json:"storage_provider,omitempty"`
@@ -106,8 +112,12 @@ func (s *DocumentService) CreateDocument(ctx context.Context, req CreateDocument
}
input := models.DocumentInput{
Title: title,
URL: url,
Title: title,
URL: url,
ReadMode: req.ReadMode,
AllowDownload: req.AllowDownload,
RequireFullRead: req.RequireFullRead,
VerifyChecksum: req.VerifyChecksum,
}
// Handle storage fields if provided (for uploaded files)

View File

@@ -160,6 +160,15 @@ func (h *Handler) HandleUpload(w http.ResponseWriter, r *http.Request) {
title = header.Filename
}
// Get reader options from form
readMode := r.FormValue("readMode")
if readMode == "" {
readMode = "integrated"
}
allowDownload := r.FormValue("allowDownload") == "true"
requireFullRead := r.FormValue("requireFullRead") == "true"
verifyChecksum := r.FormValue("verifyChecksum") != "false" // default true
// Detect content type from file content
buffer := make([]byte, 512)
n, err := file.Read(buffer)
@@ -207,6 +216,10 @@ func (h *Handler) HandleUpload(w http.ResponseWriter, r *http.Request) {
Reference: storageKey,
Title: title,
CreatedBy: user.Email,
ReadMode: readMode,
AllowDownload: &allowDownload,
RequireFullRead: &requireFullRead,
VerifyChecksum: &verifyChecksum,
StorageKey: storageKey,
StorageProvider: h.provider.Type(),
FileSize: header.Size,

View File

@@ -5,18 +5,24 @@ import (
"context"
"strings"
"github.com/btouchard/ackify-ce/backend/pkg/models"
"github.com/btouchard/ackify-ce/backend/pkg/providers"
)
// ConfigProvider provides access to general configuration.
type ConfigProvider interface {
GetConfig() *models.MutableConfig
}
// SimpleAuthorizer is an authorization implementation based on a list of admin emails.
// This is the default authorizer for Community Edition.
type SimpleAuthorizer struct {
adminEmails map[string]bool
onlyAdminCanCreate bool
adminEmails map[string]bool
configProvider ConfigProvider
}
// NewSimpleAuthorizer creates a new simple authorizer.
func NewSimpleAuthorizer(adminEmails []string, onlyAdminCanCreate bool) *SimpleAuthorizer {
func NewSimpleAuthorizer(adminEmails []string, configProvider ConfigProvider) *SimpleAuthorizer {
emailMap := make(map[string]bool, len(adminEmails))
for _, email := range adminEmails {
normalized := strings.ToLower(strings.TrimSpace(email))
@@ -25,8 +31,8 @@ func NewSimpleAuthorizer(adminEmails []string, onlyAdminCanCreate bool) *SimpleA
}
}
return &SimpleAuthorizer{
adminEmails: emailMap,
onlyAdminCanCreate: onlyAdminCanCreate,
adminEmails: emailMap,
configProvider: configProvider,
}
}
@@ -38,7 +44,8 @@ func (a *SimpleAuthorizer) IsAdmin(_ context.Context, userEmail string) bool {
// CanCreateDocument implements providers.Authorizer.
func (a *SimpleAuthorizer) CanCreateDocument(ctx context.Context, userEmail string) bool {
if !a.onlyAdminCanCreate {
cfg := a.configProvider.GetConfig()
if !cfg.General.OnlyAdminCanCreate {
return true
}
return a.IsAdmin(ctx, userEmail)

View File

@@ -225,7 +225,7 @@ func (b *ServerBuilder) setDefaultProviders() {
})
}
if b.authorizer == nil {
b.authorizer = webauth.NewSimpleAuthorizer(b.cfg.App.AdminEmails, b.cfg.App.OnlyAdminCanCreate)
b.authorizer = webauth.NewSimpleAuthorizer(b.cfg.App.AdminEmails, b.configService)
}
if b.quotaEnforcer == nil {
b.quotaEnforcer = NewNoLimitQuotaEnforcer()

View File

@@ -159,7 +159,13 @@ async function handleSubmit() {
isUploading.value = true
const uploadResponse = await documentService.uploadDocument(
selectedFile.value,
title.value || undefined,
{
title: title.value || undefined,
readMode: readMode.value,
allowDownload: allowDownload.value,
requireFullRead: requireFullRead.value,
verifyChecksum: verifyChecksum.value,
},
(progress) => {
uploadProgress.value = progress
}

View File

@@ -26,6 +26,14 @@ export interface UploadProgress {
percent: number
}
export interface UploadDocumentOptions {
title?: string
readMode?: 'integrated' | 'external'
allowDownload?: boolean
requireFullRead?: boolean
verifyChecksum?: boolean
}
export interface CreateDocumentResponse {
docId: string
url?: string
@@ -139,13 +147,13 @@ export const documentService = {
/**
* Upload a file and create a document
* @param file File to upload
* @param title Optional title for the document
* @param options Upload options including title and reader settings
* @param onProgress Optional callback for upload progress
* @returns Upload response with document info
*/
async uploadDocument(
file: File,
title?: string,
options?: UploadDocumentOptions,
onProgress?: (progress: UploadProgress) => void
): Promise<UploadDocumentResponse> {
// Get CSRF token first
@@ -154,8 +162,20 @@ export const documentService = {
const formData = new FormData()
formData.append('file', file)
if (title) {
formData.append('title', title)
if (options?.title) {
formData.append('title', options.title)
}
if (options?.readMode) {
formData.append('readMode', options.readMode)
}
if (options?.allowDownload !== undefined) {
formData.append('allowDownload', String(options.allowDownload))
}
if (options?.requireFullRead !== undefined) {
formData.append('requireFullRead', String(options.requireFullRead))
}
if (options?.verifyChecksum !== undefined) {
formData.append('verifyChecksum', String(options.verifyChecksum))
}
const response = await axios.post<ApiResponse<UploadDocumentResponse>>(