// Package handler — virtualviewhandler.go: GET dispatch for the // aggregate register rows (mdl//*.yaml, rsk//*.yaml, // ssr/.yaml). // // In the flat-peer layout these are REAL files at their own paths; the // only thing the on-disk bytes lack is the path-derived source column // the aggregate table renders: // // - MDL / RSK rows get `$party: ` so the cross-party rollup // can show which party each row came from. The `$` sigil marks the // field system-synthesised: the tables tool renders it read-only and // the form client strips it before submit, so a user-defined `party` // field never collides with the synthetic source-party column. // - SSR rows get `name: ` (the party = the filename) so the // register table has an identity column to sort on and the form edit // pre-fills the party name. // // Both fields are path-derived and stripped before write-back by the // tables/form JS (the schema's additionalProperties:false also rejects // `$party` on submit). Listings: see fs/tree.go. package handler import ( "net/http" "os" "strconv" "gopkg.in/yaml.v3" ) // ServeInjectedRow serves a GET/HEAD for a real register row file with a // single path-derived field injected (field=value). Used by dispatch for // the aggregate register rows; ACL is evaluated by the caller against the // file's own chain. Returns 404 if the file doesn't exist. func ServeInjectedRow(w http.ResponseWriter, r *http.Request, abs, field, value string) { raw, err := os.ReadFile(abs) if err != nil { if os.IsNotExist(err) { http.NotFound(w, r) return } http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } var data map[string]any if len(raw) > 0 { if err := yaml.Unmarshal(raw, &data); err != nil { http.Error(w, "parse row yaml: "+err.Error(), http.StatusInternalServerError) return } } if data == nil { data = make(map[string]any) } data[field] = value out, err := yaml.Marshal(data) if err != nil { http.Error(w, "marshal row: "+err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/yaml; charset=utf-8") w.Header().Set("Cache-Control", "no-store") w.Header().Set("X-ZDDC-Source", "register-row") w.Header().Set("Content-Length", strconv.Itoa(len(out))) if r.Method == http.MethodHead { w.WriteHeader(http.StatusOK) return } _, _ = w.Write(out) }