Skip to content

Commit bff75aa

Browse files
committed
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
1 parent 92f97ef commit bff75aa

File tree

6 files changed

+70
-14
lines changed

6 files changed

+70
-14
lines changed

backend/internal/application/services/document_service.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@ type CreateDocumentRequest struct {
5555
Title string `json:"title"`
5656
CreatedBy string `json:"created_by,omitempty"`
5757

58+
// Reader options
59+
ReadMode string `json:"read_mode,omitempty"`
60+
AllowDownload *bool `json:"allow_download,omitempty"`
61+
RequireFullRead *bool `json:"require_full_read,omitempty"`
62+
VerifyChecksum *bool `json:"verify_checksum,omitempty"`
63+
5864
// Storage fields for uploaded files
5965
StorageKey string `json:"storage_key,omitempty"`
6066
StorageProvider string `json:"storage_provider,omitempty"`
@@ -106,8 +112,12 @@ func (s *DocumentService) CreateDocument(ctx context.Context, req CreateDocument
106112
}
107113

108114
input := models.DocumentInput{
109-
Title: title,
110-
URL: url,
115+
Title: title,
116+
URL: url,
117+
ReadMode: req.ReadMode,
118+
AllowDownload: req.AllowDownload,
119+
RequireFullRead: req.RequireFullRead,
120+
VerifyChecksum: req.VerifyChecksum,
111121
}
112122

113123
// Handle storage fields if provided (for uploaded files)

backend/internal/presentation/api/storage/handler.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,15 @@ func (h *Handler) HandleUpload(w http.ResponseWriter, r *http.Request) {
160160
title = header.Filename
161161
}
162162

163+
// Get reader options from form
164+
readMode := r.FormValue("readMode")
165+
if readMode == "" {
166+
readMode = "integrated"
167+
}
168+
allowDownload := r.FormValue("allowDownload") == "true"
169+
requireFullRead := r.FormValue("requireFullRead") == "true"
170+
verifyChecksum := r.FormValue("verifyChecksum") != "false" // default true
171+
163172
// Detect content type from file content
164173
buffer := make([]byte, 512)
165174
n, err := file.Read(buffer)
@@ -207,6 +216,10 @@ func (h *Handler) HandleUpload(w http.ResponseWriter, r *http.Request) {
207216
Reference: storageKey,
208217
Title: title,
209218
CreatedBy: user.Email,
219+
ReadMode: readMode,
220+
AllowDownload: &allowDownload,
221+
RequireFullRead: &requireFullRead,
222+
VerifyChecksum: &verifyChecksum,
210223
StorageKey: storageKey,
211224
StorageProvider: h.provider.Type(),
212225
FileSize: header.Size,

backend/pkg/web/auth/simple_authorizer.go

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,24 @@ import (
55
"context"
66
"strings"
77

8+
"github.com/btouchard/ackify-ce/backend/pkg/models"
89
"github.com/btouchard/ackify-ce/backend/pkg/providers"
910
)
1011

12+
// ConfigProvider provides access to general configuration.
13+
type ConfigProvider interface {
14+
GetConfig() *models.MutableConfig
15+
}
16+
1117
// SimpleAuthorizer is an authorization implementation based on a list of admin emails.
1218
// This is the default authorizer for Community Edition.
1319
type SimpleAuthorizer struct {
14-
adminEmails map[string]bool
15-
onlyAdminCanCreate bool
20+
adminEmails map[string]bool
21+
configProvider ConfigProvider
1622
}
1723

1824
// NewSimpleAuthorizer creates a new simple authorizer.
19-
func NewSimpleAuthorizer(adminEmails []string, onlyAdminCanCreate bool) *SimpleAuthorizer {
25+
func NewSimpleAuthorizer(adminEmails []string, configProvider ConfigProvider) *SimpleAuthorizer {
2026
emailMap := make(map[string]bool, len(adminEmails))
2127
for _, email := range adminEmails {
2228
normalized := strings.ToLower(strings.TrimSpace(email))
@@ -25,8 +31,8 @@ func NewSimpleAuthorizer(adminEmails []string, onlyAdminCanCreate bool) *SimpleA
2531
}
2632
}
2733
return &SimpleAuthorizer{
28-
adminEmails: emailMap,
29-
onlyAdminCanCreate: onlyAdminCanCreate,
34+
adminEmails: emailMap,
35+
configProvider: configProvider,
3036
}
3137
}
3238

@@ -38,7 +44,8 @@ func (a *SimpleAuthorizer) IsAdmin(_ context.Context, userEmail string) bool {
3844

3945
// CanCreateDocument implements providers.Authorizer.
4046
func (a *SimpleAuthorizer) CanCreateDocument(ctx context.Context, userEmail string) bool {
41-
if !a.onlyAdminCanCreate {
47+
cfg := a.configProvider.GetConfig()
48+
if !cfg.General.OnlyAdminCanCreate {
4249
return true
4350
}
4451
return a.IsAdmin(ctx, userEmail)

backend/pkg/web/server.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ func (b *ServerBuilder) setDefaultProviders() {
225225
})
226226
}
227227
if b.authorizer == nil {
228-
b.authorizer = webauth.NewSimpleAuthorizer(b.cfg.App.AdminEmails, b.cfg.App.OnlyAdminCanCreate)
228+
b.authorizer = webauth.NewSimpleAuthorizer(b.cfg.App.AdminEmails, b.configService)
229229
}
230230
if b.quotaEnforcer == nil {
231231
b.quotaEnforcer = NewNoLimitQuotaEnforcer()

webapp/src/components/DocumentCreateForm.vue

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,13 @@ async function handleSubmit() {
159159
isUploading.value = true
160160
const uploadResponse = await documentService.uploadDocument(
161161
selectedFile.value,
162-
title.value || undefined,
162+
{
163+
title: title.value || undefined,
164+
readMode: readMode.value,
165+
allowDownload: allowDownload.value,
166+
requireFullRead: requireFullRead.value,
167+
verifyChecksum: verifyChecksum.value,
168+
},
163169
(progress) => {
164170
uploadProgress.value = progress
165171
}

webapp/src/services/documents.ts

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ export interface UploadProgress {
2626
percent: number
2727
}
2828

29+
export interface UploadDocumentOptions {
30+
title?: string
31+
readMode?: 'integrated' | 'external'
32+
allowDownload?: boolean
33+
requireFullRead?: boolean
34+
verifyChecksum?: boolean
35+
}
36+
2937
export interface CreateDocumentResponse {
3038
docId: string
3139
url?: string
@@ -139,13 +147,13 @@ export const documentService = {
139147
/**
140148
* Upload a file and create a document
141149
* @param file File to upload
142-
* @param title Optional title for the document
150+
* @param options Upload options including title and reader settings
143151
* @param onProgress Optional callback for upload progress
144152
* @returns Upload response with document info
145153
*/
146154
async uploadDocument(
147155
file: File,
148-
title?: string,
156+
options?: UploadDocumentOptions,
149157
onProgress?: (progress: UploadProgress) => void
150158
): Promise<UploadDocumentResponse> {
151159
// Get CSRF token first
@@ -154,8 +162,20 @@ export const documentService = {
154162

155163
const formData = new FormData()
156164
formData.append('file', file)
157-
if (title) {
158-
formData.append('title', title)
165+
if (options?.title) {
166+
formData.append('title', options.title)
167+
}
168+
if (options?.readMode) {
169+
formData.append('readMode', options.readMode)
170+
}
171+
if (options?.allowDownload !== undefined) {
172+
formData.append('allowDownload', String(options.allowDownload))
173+
}
174+
if (options?.requireFullRead !== undefined) {
175+
formData.append('requireFullRead', String(options.requireFullRead))
176+
}
177+
if (options?.verifyChecksum !== undefined) {
178+
formData.append('verifyChecksum', String(options.verifyChecksum))
159179
}
160180

161181
const response = await axios.post<ApiResponse<UploadDocumentResponse>>(

0 commit comments

Comments
 (0)