feat: Lightroom-style workspace UI #2
@@ -1,6 +1,7 @@
|
||||
package photos
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
@@ -12,7 +13,7 @@ import (
|
||||
"shared-album/internal/httpx"
|
||||
)
|
||||
|
||||
const maxUploadSize = 50 << 20
|
||||
const maxUploadSize = 100 << 20
|
||||
|
||||
type Handler struct {
|
||||
svc *Service
|
||||
@@ -43,7 +44,12 @@ func (h *Handler) Upload(c *gin.Context) {
|
||||
c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, maxUploadSize)
|
||||
form, err := c.MultipartForm()
|
||||
if err != nil {
|
||||
httpx.RespondError(c, httpx.BadRequest("invalid_request", "expected multipart form"))
|
||||
var maxErr *http.MaxBytesError
|
||||
if errors.As(err, &maxErr) {
|
||||
httpx.RespondError(c, httpx.NewError(http.StatusRequestEntityTooLarge, "file_too_large", "files exceed the maximum upload size (100 MB)"))
|
||||
return
|
||||
}
|
||||
httpx.RespondError(c, httpx.BadRequest("invalid_request", "invalid multipart form"))
|
||||
return
|
||||
}
|
||||
files := form.File["files"]
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
let progress = $state(0);
|
||||
let error = $state('');
|
||||
|
||||
const MAX_SIZE = 100 * 1024 * 1024;
|
||||
|
||||
export function pick() {
|
||||
fileInput?.click();
|
||||
}
|
||||
@@ -21,6 +23,16 @@
|
||||
export function upload(files: FileList | File[]) {
|
||||
const list = Array.from(files);
|
||||
if (list.length === 0) return;
|
||||
const tooBig = list.filter((f) => f.size > MAX_SIZE);
|
||||
if (tooBig.length > 0) {
|
||||
error = `Файл слишком большой: ${tooBig.map((f) => f.name).join(', ')}. Максимум 100 МБ.`;
|
||||
return;
|
||||
}
|
||||
const total = list.reduce((sum, f) => sum + f.size, 0);
|
||||
if (total > MAX_SIZE) {
|
||||
error = 'Суммарный размер файлов превышает 100 МБ. Загрузите меньшими порциями.';
|
||||
return;
|
||||
}
|
||||
error = '';
|
||||
busy = true;
|
||||
progress = 0;
|
||||
|
||||
Reference in New Issue
Block a user