| 1 | package sheets |
| 2 | |
| 3 | import ( |
| 4 | "encoding/json" |
| 5 | "io" |
| 6 | "net/http" |
| 7 | |
| 8 | "amadan.net/oficina/sheets/internal/sheets/store" |
| 9 | ) |
| 10 | |
| 11 | func (a *App) handleCellBatchPost(w http.ResponseWriter, r *http.Request) { |
| 12 | actor, ok := a.actor(r) |
| 13 | if !ok { |
| 14 | http.Error(w, "Sign in first.", http.StatusForbidden) |
| 15 | return |
| 16 | } |
| 17 | h, ok := a.openTab(w, r, SheetEditor) |
| 18 | if !ok { |
| 19 | return |
| 20 | } |
| 21 | defer h.Release() |
| 22 | |
| 23 | var in struct { |
| 24 | Cells []cellWrite `json:"cells"` |
| 25 | Client string `json:"client"` |
| 26 | } |
| 27 | dec := json.NewDecoder(http.MaxBytesReader(w, r.Body, 1<<20)) |
| 28 | if err := dec.Decode(&in); err != nil { |
| 29 | http.Error(w, "Malformed request.", http.StatusBadRequest) |
| 30 | return |
| 31 | } |
| 32 | var extra any |
| 33 | if err := dec.Decode(&extra); err != io.EOF || len(in.Cells) == 0 || len(in.Cells) > store.MaxCellBatch { |
| 34 | http.Error(w, "Malformed request.", http.StatusBadRequest) |
| 35 | return |
| 36 | } |
| 37 | writes := make([]store.CellWrite, len(in.Cells)) |
| 38 | for i, cell := range in.Cells { |
| 39 | dr, dc, valid := cell.origin() |
| 40 | if !inCellRange(cell.R, cell.C) || !valid || (cell.FromR == nil) != (cell.FromC == nil) { |
| 41 | http.Error(w, "Cell out of range.", http.StatusBadRequest) |
| 42 | return |
| 43 | } |
| 44 | if len(cell.Raw) > maxRawBytes { |
| 45 | http.Error(w, "Cell input too long.", http.StatusRequestEntityTooLarge) |
| 46 | return |
| 47 | } |
| 48 | raw := translateCopied(cell.Raw, dr, dc) |
| 49 | if len(raw) > maxRawBytes { |
| 50 | http.Error(w, "Cell input too long.", http.StatusRequestEntityTooLarge) |
| 51 | return |
| 52 | } |
| 53 | writes[i] = store.CellWrite{Row: cell.R, Col: cell.C, Raw: raw} |
| 54 | } |
| 55 | rep, err := h.SetBatch(writes, actor.ID) |
| 56 | if err != nil { |
| 57 | http.Error(w, "That cell could not be written.", http.StatusBadRequest) |
| 58 | return |
| 59 | } |
| 60 | cells, styles := fromReport(rep, newPosIndex(h), h) |
| 61 | resp := gridResponse{Version: rep.Version, Cells: cells, Formats: styles, Stamps: rep.Stamps, Client: in.Client} |
| 62 | a.touchSheet(sheetIDFrom(r)) |
| 63 | a.publish(tabIDFrom(r), resp) |
| 64 | writeJSON(w, http.StatusOK, resp) |
| 65 | } |
| 66 | |