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
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:
1 parent
9b50038bb4
commit
f66be2ba0a
27 files changed
+2422
-213
No files matched your search
@@ -63,7 +63,7 @@ func (h *Handler) sourceDTO(s Source) gen.Source {
|
||||
Id: s.ID, Name: s.Name, Type: gen.SourceType(s.Type), NfsServer: s.NFSServer, NfsExport: s.NFSExport,
|
||||
NfsOptions: s.NFSOptions, LocalPath: s.LocalPath, TagPriority: gen.TagsFirst,
|
||||
Status: gen.SourceStatus(s.Status), StatusMessage: s.StatusMessage, LastScanAt: s.LastScanAt,
|
||||
CreatedAt: s.CreatedAt,
|
||||
CreatedAt: s.CreatedAt, AcceptsUploads: s.AcceptsUploads, WriteTemplate: s.WriteTemplate,
|
||||
}
|
||||
if !s.TagsFirst {
|
||||
out.TagPriority = gen.PathFirst
|
||||
@@ -118,6 +118,7 @@ func (h *Handler) CreateSource(ctx context.Context, req gen.CreateSourceRequestO
|
||||
src, err := h.svc.CreateSource(ctx, SourceInput{
|
||||
Name: &b.Name, Type: SourceType(b.Type), NFSServer: b.NfsServer, NFSExport: b.NfsExport, NFSOptions: b.NfsOptions,
|
||||
LocalPath: b.LocalPath, TagsFirst: tagsFirst(b.TagPriority), ScanIntervalMinutes: b.ScanIntervalMinutes,
|
||||
AcceptsUploads: b.AcceptsUploads, WriteTemplate: b.WriteTemplate,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -140,6 +141,7 @@ func (h *Handler) UpdateSource(ctx context.Context, req gen.UpdateSourceRequestO
|
||||
src, err := h.svc.UpdateSource(ctx, req.Id, SourceInput{
|
||||
Name: b.Name, NFSServer: b.NfsServer, NFSExport: b.NfsExport, NFSOptions: b.NfsOptions, LocalPath: b.LocalPath,
|
||||
TagsFirst: tagsFirst(b.TagPriority), ScanIntervalMinutes: b.ScanIntervalMinutes, Enabled: b.Enabled,
|
||||
AcceptsUploads: b.AcceptsUploads, WriteTemplate: b.WriteTemplate,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -161,7 +163,46 @@ func (h *Handler) TestSource(ctx context.Context, req gen.TestSourceRequestObjec
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return gen.TestSource200JSONResponse{Ok: r.OK, Message: r.Message, AudioFiles: r.AudioFiles}, nil
|
||||
out := gen.TestSource200JSONResponse{Ok: r.OK, Message: r.Message, AudioFiles: r.AudioFiles}
|
||||
if r.Write != nil {
|
||||
out.WriteOk, out.WriteMessage = &r.Write.OK, &r.Write.Message
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// SuggestWriteTemplate реализует gen.StrictServerInterface.
|
||||
func (h *Handler) SuggestWriteTemplate(ctx context.Context, req gen.SuggestWriteTemplateRequestObject) (gen.SuggestWriteTemplateResponseObject, error) {
|
||||
tpl, err := h.svc.SuggestWriteTemplate(ctx, req.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return gen.SuggestWriteTemplate200JSONResponse{Template: tpl}, nil
|
||||
}
|
||||
|
||||
// CheckWriteTemplate реализует gen.StrictServerInterface.
|
||||
func (h *Handler) CheckWriteTemplate(ctx context.Context, req gen.CheckWriteTemplateRequestObject) (gen.CheckWriteTemplateResponseObject, error) {
|
||||
res, err := h.svc.CheckWriteTemplate(ctx, req.Id, req.Body.Template)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := gen.CheckWriteTemplate200JSONResponse{
|
||||
Ok: res.OK, Total: res.Total, RoundTrip: res.RoundTrip, SamePath: res.SamePath,
|
||||
Mismatches: make([]gen.WriteTemplateMismatch, 0, len(res.Mismatches)),
|
||||
}
|
||||
for _, m := range res.Mismatches {
|
||||
dto := gen.WriteTemplateMismatch{RelPath: m.RelPath, Fields: m.Fields}
|
||||
if dto.Fields == nil {
|
||||
dto.Fields = []string{}
|
||||
}
|
||||
if m.Rendered != "" {
|
||||
dto.Rendered = &m.Rendered
|
||||
}
|
||||
if m.Error != "" {
|
||||
dto.Error = &m.Error
|
||||
}
|
||||
out.Mismatches = append(out.Mismatches, dto)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func patternsDTO(rows []PatternRow) gen.PatternList {
|
||||
|
||||
Reference in new issue
Block a user