fix: file upload via GUI + API endpoint

- Replace stub file modal with working file path input
- PUT /api/files/upload — copies external file into vault
- submitFile() JS: posts path to API, refreshes node view
- Button '+ Файл' now opens functional modal
This commit is contained in:
mirivlad 2026-05-31 11:38:49 +08:00
parent b800bce7e4
commit 752b1bb4b8
4 changed files with 42 additions and 4 deletions

View File

@ -20,7 +20,7 @@
| 7 | Actions: Run URL/File/Command + GUI Tab | ✅ выполнен |
| 8 | Worklog: Entries + Report + GUI Tab | ✅ выполнен |
| 9 | FTS5 Search: Rebuild Index + GUI Search Bar | ✅ выполнен |
| 10 | Plugins System (Lua + Templates) | ⬜ не начат |
| 10 | Plugins System (Lua + Templates) | ✅ выполнен |
| 11 | Sync Server Skeleton | ⬜ не начат |
| 12 | Sync Client MVP | ⬜ не начат |
| 13 | Activity + File Scanner/Watcher | ⬜ не начат |

View File

@ -217,9 +217,11 @@ input[type=checkbox]{width:auto!important;margin-right:6px;display:inline}
<div class="ma"><button class="btn" onclick="closeM('m-note')">Отмена</button><button class="btn primary" onclick="submitNote()">Создать</button></div></div>
</div>
<div class="mo" id="m-file">
<div class="md"><h3>Файл</h3>
<p style="color:var(--text3);font-size:13px;line-height:1.6">MVP: добавление файлов через интерфейс пока недоступно.</p>
<div class="ma"><button class="btn primary" onclick="closeM('m-file')">Понятно</button></div></div>
<div class="md"><h3>Добавить файл</h3>
<p style="color:var(--text3);font-size:13px;line-height:1.5;margin-bottom:12px">Файл будет скопирован в vault и привязан к выбранному делу.</p>
<label>Или вставьте путь вручную</label>
<input id="mf-path" placeholder="/home/user/documents/file.pdf">
<div class="ma"><button class="btn" onclick="closeM('m-file')">Отмена</button><button class="btn primary" onclick="submitFile()">Добавить</button></div></div>
</div>
<div class="mo" id="m-action">
<div class="md"><h3>Действие</h3>
@ -792,6 +794,17 @@ async function submitWorklog(){
}catch(e){alert('Ошибка: '+e.message)}
}
async function submitFile(){
const path=G('mf-path').value.trim();
if(!path)return;
if(!sel.nodeId){E('Выберите дело слева');return}
try{
const rec=await api('/api/files/upload',{method:'PUT',body:JSON.stringify({node_id:sel.nodeId,file_path:path,node_slug:''})});
closeM('m-file');
selectNode({dataset:{id:sel.nodeId}});
}catch(e){alert('Ошибка: '+e.message)}
}
/*
SEARCH
*/

View File

@ -59,6 +59,7 @@ func (s *Server) Start() (string, error) {
mux.HandleFunc("/api/nodes/from-template", s.handleNodeFromTemplate)
mux.HandleFunc("/api/nodes/", s.handleNodeDetail)
mux.HandleFunc("/api/notes/", s.handleNotes)
mux.HandleFunc("/api/files/upload", s.handleFileUpload)
mux.HandleFunc("/api/files/", s.handleFiles)
mux.HandleFunc("/api/actions/", s.handleActions)
mux.HandleFunc("/api/worklog/", s.handleWorklog)
@ -291,6 +292,30 @@ func (s *Server) handleNotes(w http.ResponseWriter, r *http.Request) {
}
}
// PUT /api/files/upload — register an external file into the vault.
func (s *Server) handleFileUpload(w http.ResponseWriter, r *http.Request) {
if r.Method != "PUT" {
jsonErr(w, 405, "method not allowed")
return
}
var req struct {
NodeID string `json:"node_id"`
FilePath string `json:"file_path"`
NodeSlug string `json:"node_slug"`
}
json.NewDecoder(r.Body).Decode(&req)
if req.NodeID == "" || req.FilePath == "" {
jsonErr(w, 400, "node_id and file_path required")
return
}
rec, err := s.files.CopyIntoVault(req.NodeID, req.FilePath, req.NodeSlug)
if err != nil {
jsonErr(w, 500, err.Error())
return
}
jsonOK(w, rec)
}
// GET/DELETE /api/files/{id}
func (s *Server) handleFiles(w http.ResponseWriter, r *http.Request) {
id := strings.TrimPrefix(r.URL.Path, "/api/files/")

BIN
verstak-gui Executable file

Binary file not shown.