feat: add max upload size setting to UI & UI improvements (#1646)

* Add preliminar Windows support for both
development and production environments.

Default profile.Data will be set to "C:\ProgramData\memos" on Windows.
Folder will be created if it does not exist, as this behavior is
expected for Windows applications.

System service installation can be achieved with third-party tools,
explained in docs/windows-service.md.

Not sure if it's worth using https://github.com/kardianos/service
to make service support built-in.

This could be a nice addition alongside #1583 (add Windows artifacts)

* feat: improve Windows support

- Fix local file storage path handling on Windows

- Improve Windows dev script

* feat: add max upload size setting to UI & more

- feat: add max upload size setting to UI

- feat: max upload size setting is checked on UI during upload,
but also enforced by the server

- fix: overflowing mobile layout for Create SSO, Create Storage
and other Settings dialogs

- feat: add HelpButton component with some links to docs were appropriate

- remove LearnMore component in favor of HelpButton

- refactor: change some if/else to switch statements

- refactor: inline some err == nil checks

! Existing databases without the new setting 'max-upload-size-mib'
will show an upload error, but this can be user-fixed by simply
setting the value on system settings UI.

* improvements requested by @boojack
This commit is contained in:
Lincoln Nogueira
2023-05-13 11:27:28 -03:00
committed by GitHub
parent 5c5199920e
commit 96021e518a
20 changed files with 591 additions and 204 deletions

View File

@ -25,8 +25,11 @@ import (
)
const (
// The max file size is 32MB.
maxFileSize = 32 << 20
// The upload memory buffer is 32 MiB.
// It should be kept low, so RAM usage doesn't get out of control.
// This is unrelated to maximum upload size limit, which is now set through system setting.
maxUploadBufferSizeBytes = 32 << 20
MebiByte = 1024 * 1024
)
var fileKeyPattern = regexp.MustCompile(`\{[a-z]{1,9}\}`)
@ -67,8 +70,13 @@ func (s *Server) registerResourceRoutes(g *echo.Group) {
return echo.NewHTTPError(http.StatusUnauthorized, "Missing user in session")
}
if err := c.Request().ParseMultipartForm(maxFileSize); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Upload file overload max size").SetInternal(err)
maxUploadSetting := s.Store.GetSystemSettingValueOrDefault(&ctx, api.SystemSettingMaxUploadSizeMiBName, "0")
var settingMaxUploadSizeBytes int
if settingMaxUploadSizeMiB, err := strconv.Atoi(maxUploadSetting); err == nil {
settingMaxUploadSizeBytes = settingMaxUploadSizeMiB * MebiByte
} else {
log.Warn("Failed to parse max upload size", zap.Error(err))
settingMaxUploadSizeBytes = 0
}
file, err := c.FormFile("file")
@ -79,6 +87,14 @@ func (s *Server) registerResourceRoutes(g *echo.Group) {
return echo.NewHTTPError(http.StatusBadRequest, "Upload file not found").SetInternal(err)
}
if file.Size > int64(settingMaxUploadSizeBytes) {
message := fmt.Sprintf("File size exceeds allowed limit of %d MiB", settingMaxUploadSizeBytes/MebiByte)
return echo.NewHTTPError(http.StatusBadRequest, message).SetInternal(err)
}
if err := c.Request().ParseMultipartForm(maxUploadBufferSizeBytes); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Failed to parse upload data").SetInternal(err)
}
filetype := file.Header.Get("Content-Type")
size := file.Size
sourceFile, err := file.Open()