forked from MatiasDesuu/ThinkDashboard
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathuploads.go
More file actions
243 lines (212 loc) · 6.67 KB
/
uploads.go
File metadata and controls
243 lines (212 loc) · 6.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
package main
import (
"encoding/json"
"io"
"net/http"
"os"
"path/filepath"
"strings"
)
// UploadFavicon handles favicon file uploads
func (h *Handlers) UploadFavicon(w http.ResponseWriter, r *http.Request) {
// Parse multipart form
err := r.ParseMultipartForm(10 << 20) // 10 MB max
if err != nil {
http.Error(w, "Unable to parse form", http.StatusBadRequest)
return
}
file, header, err := r.FormFile("favicon")
if err != nil {
http.Error(w, "Error retrieving file", http.StatusBadRequest)
return
}
defer file.Close()
// Validate file type (should be image)
contentType := header.Header.Get("Content-Type")
if contentType != "image/x-icon" && contentType != "image/png" && contentType != "image/jpeg" && contentType != "image/gif" {
http.Error(w, "Invalid file type. Only ico, png, jpg, gif allowed", http.StatusBadRequest)
return
}
// Create data directory if it doesn't exist
dataDir := "data"
if _, err := os.Stat(dataDir); os.IsNotExist(err) {
os.MkdirAll(dataDir, 0755)
}
// Determine file extension
var ext string
switch contentType {
case "image/x-icon":
ext = ".ico"
case "image/png":
ext = ".png"
case "image/jpeg":
ext = ".jpg"
case "image/gif":
ext = ".gif"
default:
ext = filepath.Ext(header.Filename)
}
// Save file as favicon with appropriate extension
faviconPath := filepath.Join(dataDir, "favicon"+ext)
dst, err := os.Create(faviconPath)
if err != nil {
http.Error(w, "Unable to save file", http.StatusInternalServerError)
return
}
defer dst.Close()
_, err = io.Copy(dst, file)
if err != nil {
http.Error(w, "Unable to save file", http.StatusInternalServerError)
return
}
// Update settings with the new favicon path
settings := h.store.GetSettings()
settings.CustomFaviconPath = "/data/favicon" + ext
h.store.SaveSettings(settings)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{"status": "success", "path": settings.CustomFaviconPath})
}
// UploadFont handles custom font file uploads
func (h *Handlers) UploadFont(w http.ResponseWriter, r *http.Request) {
// Parse multipart form
err := r.ParseMultipartForm(10 << 20) // 10 MB max
if err != nil {
http.Error(w, "Unable to parse form", http.StatusBadRequest)
return
}
file, header, err := r.FormFile("font")
if err != nil {
http.Error(w, "Error retrieving file", http.StatusBadRequest)
return
}
defer file.Close()
// Validate file type (should be font)
contentType := header.Header.Get("Content-Type")
filename := header.Filename
ext := strings.ToLower(filepath.Ext(filename))
validTypes := map[string]bool{
"font/woff": true,
"font/woff2": true,
"font/ttf": true,
"font/otf": true,
"application/font-woff": true,
"application/font-woff2": true,
"application/x-font-ttf": true,
"application/x-font-otf": true,
"application/font-sfnt": true,
}
isValidType := validTypes[contentType]
isValidExt := ext == ".woff" || ext == ".woff2" || ext == ".ttf" || ext == ".otf"
if !isValidType && !isValidExt {
http.Error(w, "Invalid file type. Only woff, woff2, ttf, otf allowed", http.StatusBadRequest)
return
}
// Create data directory if it doesn't exist
dataDir := "data"
if _, err := os.Stat(dataDir); os.IsNotExist(err) {
os.MkdirAll(dataDir, 0755)
}
// Determine file extension
switch contentType {
case "font/woff", "application/font-woff":
ext = ".woff"
case "font/woff2", "application/font-woff2":
ext = ".woff2"
case "font/ttf", "application/x-font-ttf", "application/font-sfnt":
ext = ".ttf"
case "font/otf", "application/x-font-otf":
ext = ".otf"
default:
// Use extension from filename if content type not recognized
if ext == "" {
ext = filepath.Ext(header.Filename)
}
}
// Save file as font with appropriate extension
fontPath := filepath.Join(dataDir, "font"+ext)
dst, err := os.Create(fontPath)
if err != nil {
http.Error(w, "Unable to save file", http.StatusInternalServerError)
return
}
defer dst.Close()
_, err = io.Copy(dst, file)
if err != nil {
http.Error(w, "Unable to save file", http.StatusInternalServerError)
return
}
// Update settings with the new font path
settings := h.store.GetSettings()
settings.CustomFontPath = "/data/font" + ext
h.store.SaveSettings(settings)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{"status": "success", "path": settings.CustomFontPath})
}
// UploadIcon handles bookmark icon file uploads
func (h *Handlers) UploadIcon(w http.ResponseWriter, r *http.Request) {
// Parse multipart form
err := r.ParseMultipartForm(10 << 20) // 10 MB max
if err != nil {
http.Error(w, "Unable to parse form", http.StatusBadRequest)
return
}
file, header, err := r.FormFile("icon")
if err != nil {
http.Error(w, "Error retrieving file", http.StatusBadRequest)
return
}
defer file.Close()
// Validate file type (should be image)
contentType := header.Header.Get("Content-Type")
if contentType != "image/x-icon" && contentType != "image/png" && contentType != "image/jpeg" && contentType != "image/gif" && contentType != "image/svg+xml" {
http.Error(w, "Invalid file type. Only ico, png, jpg, gif, svg allowed", http.StatusBadRequest)
return
}
// Create data/icons directory if it doesn't exist
iconsDir := "data/icons"
if _, err := os.Stat(iconsDir); os.IsNotExist(err) {
os.MkdirAll(iconsDir, 0755)
}
// Determine file extension
var ext string
switch contentType {
case "image/x-icon":
ext = ".ico"
case "image/png":
ext = ".png"
case "image/jpeg":
ext = ".jpg"
case "image/gif":
ext = ".gif"
case "image/svg+xml":
ext = ".svg"
default:
ext = filepath.Ext(header.Filename)
}
// Generate unique filename based on original filename (without extension)
baseName := strings.TrimSuffix(header.Filename, filepath.Ext(header.Filename))
// Sanitize filename to prevent path traversal
baseName = strings.ReplaceAll(baseName, "..", "")
baseName = strings.ReplaceAll(baseName, "/", "")
baseName = strings.ReplaceAll(baseName, "\\", "")
// Check if file already exists
fileName := baseName + ext
filePath := filepath.Join(iconsDir, fileName)
if _, err := os.Stat(filePath); os.IsNotExist(err) {
// File doesn't exist, save it
dst, err := os.Create(filePath)
if err != nil {
http.Error(w, "Unable to save file", http.StatusInternalServerError)
return
}
defer dst.Close()
_, err = io.Copy(dst, file)
if err != nil {
http.Error(w, "Unable to save file", http.StatusInternalServerError)
return
}
}
// If file exists, we reuse it (no need to save again)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{"status": "success", "icon": fileName})
}