feat: storage writes via storage-agent, accepts_uploads and write_template (M5.5)
ci / api / api (push) Successful in 33s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 43s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m11s
ci / ts / ts (packages + web) (push) Successful in 1m10s
ci / docker / docker (образы собираются — их сервер собирает сам при обновлении) (push) Successful in 51s

- storage-agent: POST /internal/files copies a staged file into a source
  (temp file, fsync, SHA-256 check, hard link so nothing is overwritten,
  " (2)" suffix on conflict, owner PIPU_FILE_UID/GID) and
  POST /internal/files/test. NFS sources that accept uploads are mounted
  rw in an agent-only /library-rw and bind-mounted read-only for others;
  local sources are written through /library-host-rw.
- meta: write templates ({track:02}, {disc:1-}, [optional]) with name
  sanitising and length limits, suggestion from the source's regexes
  and a reversibility check against 50 catalogue tracks.
- API and admin UI: accepts_uploads / write_template on sources,
  write-template suggest and check endpoints, write test result.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
mrleo1nidandClaude Opus 5.5 committed 2026-10-20 13:46:56 +03:00
1 parent 9b50038bb4
commit f66be2ba0a
27 files changed
+2422 -213

No files matched your search

+26 -4
View File
@@ -13,11 +13,14 @@ import (
"pipu/api/internal/storageagent"
)
// Agent — клиент storage-agent (монтирование NFS).
// Agent — клиент storage-agent (монтирование NFS, запись импортированных файлов).
type Agent interface {
Mount(ctx context.Context, sourceID uuid.UUID, spec storageagent.NFSSpec) (storageagent.MountResult, error)
// Mount монтирует NFS; writable — источник принимает загрузки (ещё rw-точка, видимая только агенту).
Mount(ctx context.Context, sourceID uuid.UUID, spec storageagent.NFSSpec, writable bool) (storageagent.MountResult, error)
Unmount(ctx context.Context, sourceID uuid.UUID) error
Test(ctx context.Context, spec storageagent.NFSSpec) (storageagent.MountResult, error)
TestWrite(ctx context.Context, req storageagent.WriteTestRequest) (storageagent.WriteTestResult, error)
WriteFile(ctx context.Context, req storageagent.WriteRequest) (storageagent.WriteResult, error)
}
// HTTPAgent ходит в storage-agent по внутренней сети с общим секретом.
@@ -86,9 +89,28 @@ func (a *HTTPAgent) do(ctx context.Context, method, path string, body any, out a
}
// Mount реализует Agent.
func (a *HTTPAgent) Mount(ctx context.Context, id uuid.UUID, spec storageagent.NFSSpec) (storageagent.MountResult, error) {
func (a *HTTPAgent) Mount(ctx context.Context, id uuid.UUID, spec storageagent.NFSSpec, writable bool) (storageagent.MountResult, error) {
var res storageagent.MountResult
err := a.do(ctx, http.MethodPost, "/internal/mounts", storageagent.MountRequest{SourceID: id.String(), NFSSpec: spec}, &res)
err := a.do(ctx, http.MethodPost, "/internal/mounts",
storageagent.MountRequest{SourceID: id.String(), NFSSpec: spec, Writable: writable}, &res)
return res, err
}
// TestWrite реализует Agent.
func (a *HTTPAgent) TestWrite(ctx context.Context, req storageagent.WriteTestRequest) (storageagent.WriteTestResult, error) {
var res storageagent.WriteTestResult
err := a.do(ctx, http.MethodPost, "/internal/files/test", req, &res)
return res, err
}
// WriteFile реализует Agent. Копирование большого файла по NFS может идти минуты — отдельный таймаут.
func (a *HTTPAgent) WriteFile(ctx context.Context, req storageagent.WriteRequest) (storageagent.WriteResult, error) {
var res storageagent.WriteResult
ctx, cancel := context.WithTimeout(ctx, 30*time.Minute)
defer cancel()
c := *a
c.Client = &http.Client{}
err := c.do(ctx, http.MethodPost, "/internal/files", req, &res)
return res, err
}