package lib // ============================================================================= // LEGACY STUBS — temporary bridges while callers are migrated to dbcore.go // // Each stub logs its call so we can find and replace callers. // Delete this file when all callers use the new core. // ============================================================================= import ( "database/sql" "encoding/json" "fmt" "log" "os" ) // --- Legacy types --- type EntryFilter struct { DossierID string Type string Value string SearchKey string FromDate int64 ToDate int64 Limit int } type DossierFilter struct { DateOfBirth string Limit int } type AccessFilter struct { AccessorID string TargetID string Status *int } type DossierQueryRow struct { Dossier Category int `db:"category"` EntryCount int `db:"entry_count"` } type DossierAccess struct { Access } // --- Entry stubs --- func EntryAdd(e *Entry) error { log.Printf("[STUB] EntryAdd(entry=%s, dossier=%s, cat=%d)", e.EntryID, e.DossierID, e.Category) if e.EntryID == "" { e.EntryID = NewID() } return entrySave(e) } func EntryAddBatch(entries []*Entry) error { log.Printf("[STUB] EntryAddBatch(n=%d)", len(entries)) for _, e := range entries { if e.EntryID == "" { e.EntryID = NewID() } if err := entrySave(e); err != nil { return err } } return nil } func EntryAddBatchValues(entries []Entry) error { log.Printf("[STUB] EntryAddBatchValues(n=%d)", len(entries)) for i := range entries { if entries[i].EntryID == "" { entries[i].EntryID = NewID() } if err := entrySave(&entries[i]); err != nil { return err } } return nil } func EntryGet(ctx *AccessContext, id string) (*Entry, error) { accessorID := "" if ctx != nil { accessorID = ctx.AccessorID } return entryGetByID(accessorID, id) } func EntryQuery(ctx *AccessContext, dossierID string, category int, typ, parent string) ([]*Entry, error) { accessorID := "" if ctx != nil { accessorID = ctx.AccessorID } f := &Filter{Category: category} if typ != "" { f.Type = typ } if parent != "" && parent != "*" { f.ParentID = parent } return EntryRead(accessorID, dossierID, f) } func EntryList(accessorID string, parent string, category int, f *EntryFilter) ([]*Entry, error) { dossierID := "" filter := &Filter{Category: category, ParentID: parent} if f != nil { dossierID = f.DossierID filter.Type = f.Type filter.SearchKey = f.SearchKey filter.FromDate = f.FromDate filter.ToDate = f.ToDate filter.Limit = f.Limit } return EntryRead(accessorID, dossierID, filter) } func EntryQueryOld(dossierID string, category int, typ string) ([]*Entry, error) { return EntryRead("", dossierID, &Filter{Category: category, Type: typ}) } func EntryCount(ctx *AccessContext, dossierID string, category int, typ string) (int, error) { entries, err := EntryQuery(ctx, dossierID, category, typ, "*") if err != nil { return 0, err } return len(entries), nil } func EntryModify(e *Entry) error { log.Printf("[STUB] EntryModify(entry=%s)", e.EntryID) return entrySave(e) } func EntryDelete(entryID string) error { log.Printf("[STUB] EntryDelete(id=%s)", entryID) _, err := db.Exec("DELETE FROM entries WHERE EntryID = ?", entryID) return err } func EntryDeleteTree(ctx *AccessContext, dossierID, entryID string) error { log.Printf("[STUB] EntryDeleteTree(dossier=%s, entry=%s)", dossierID, entryID) // Delete children first children, _ := entryQuery(dossierID, &Filter{Category: -1, ParentID: entryID}) for _, c := range children { db.Exec("DELETE FROM entries WHERE EntryID = ?", c.EntryID) } _, err := db.Exec("DELETE FROM entries WHERE EntryID = ?", entryID) return err } func EntryDeleteByCategory(ctx *AccessContext, dossierID string, category int) error { log.Printf("[STUB] EntryDeleteByCategory(dossier=%s, cat=%d)", dossierID, category) _, err := db.Exec("DELETE FROM entries WHERE DossierID = ? AND Category = ?", dossierID, category) return err } func EntryRemoveByDossier(ctx *AccessContext, dossierID string) error { log.Printf("[STUB] EntryRemoveByDossier(dossier=%s)", dossierID) _, err := db.Exec("DELETE FROM entries WHERE DossierID = ?", dossierID) return err } func EntryChildren(dossierID, parentID string) ([]*Entry, error) { return EntryRead("", dossierID, &Filter{Category: -1, ParentID: parentID}) } func EntryChildrenByType(dossierID, parentID string, typ string) ([]*Entry, error) { return EntryRead("", dossierID, &Filter{Category: -1, ParentID: parentID, Type: typ}) } func EntryTypes(dossierID string, category int) ([]string, error) { entries, err := EntryRead("", dossierID, &Filter{Category: category}) if err != nil { return nil, err } seen := map[string]bool{} var types []string for _, e := range entries { if !seen[e.Type] { seen[e.Type] = true types = append(types, e.Type) } } return types, nil } // --- Dossier stubs --- func DossierGet(ctx *AccessContext, id string) (*Dossier, error) { accessorID := "" if ctx != nil { accessorID = ctx.AccessorID } entries, err := EntryRead(accessorID, id, &Filter{Category: 0}) if err != nil || len(entries) == 0 { return nil, fmt.Errorf("dossier not found: %s", id) } e := entries[0] d := &Dossier{DossierID: e.DossierID, Name: e.Summary, Email: e.SearchKey} if e.Data != "" { var data struct { DOB string `json:"dob"` Sex int `json:"sex"` Lang string `json:"lang"` } if json.Unmarshal([]byte(e.Data), &data) == nil { d.DateOfBirth = data.DOB d.Sex = data.Sex if data.Lang != "" { d.Preferences.Language = data.Lang } } } return d, nil } func DossierGetFirst(ctx *AccessContext) (*Dossier, error) { log.Printf("[STUB] DossierGetFirst") return &Dossier{}, nil } func DossierGetByEmail(ctx *AccessContext, email string) (*Dossier, error) { log.Printf("[STUB] DossierGetByEmail(email=%s)", email) return nil, fmt.Errorf("stub: not implemented") } func DossierGetBySessionToken(token string) *Dossier { log.Printf("[STUB] DossierGetBySessionToken") return nil } func DossierWrite(ctx *AccessContext, dossiers ...*Dossier) error { log.Printf("[STUB] DossierWrite(n=%d)", len(dossiers)) return nil } func DossierList(ctx *AccessContext, f *DossierFilter) ([]*Dossier, error) { log.Printf("[STUB] DossierList") return nil, nil } func DossierQuery(accessorID string) ([]*DossierQueryRow, error) { // Get all accessible dossier profiles via RBAC dossierEntries, err := EntryRead(accessorID, "", &Filter{Category: 0}) if err != nil { return nil, err } var rows []*DossierQueryRow for _, de := range dossierEntries { d := Dossier{DossierID: de.DossierID, Name: de.Summary, Email: de.SearchKey} if de.Data != "" { var data struct { DOB string `json:"dob"` Sex int `json:"sex"` Lang string `json:"lang"` } if json.Unmarshal([]byte(de.Data), &data) == nil { d.DateOfBirth = data.DOB d.Sex = data.Sex if data.Lang != "" { d.Preferences.Language = data.Lang } } } // Count entries by category for this dossier allEntries, err := EntryRead(accessorID, de.DossierID, &Filter{Category: -1}) if err != nil { continue } catCounts := map[int]int{} for _, e := range allEntries { if e.Category > 0 { catCounts[e.Category]++ } } if len(catCounts) == 0 { // Still include dossier even with no entries rows = append(rows, &DossierQueryRow{Dossier: d}) } else { for cat, count := range catCounts { rows = append(rows, &DossierQueryRow{Dossier: d, Category: cat, EntryCount: count}) } } } return rows, nil } func DossierSetAuthCode(dossierID string, code int, expiresAt int64) error { log.Printf("[STUB] DossierSetAuthCode(dossier=%s)", dossierID) return nil } func DossierClearAuthCode(dossierID string) error { log.Printf("[STUB] DossierClearAuthCode(dossier=%s)", dossierID) return nil } func DossierSetSessionToken(dossierID string, token string) error { log.Printf("[STUB] DossierSetSessionToken(dossier=%s)", dossierID) return nil } // --- Access stubs --- func AccessGet(accessorID, targetID string) (*Access, error) { log.Printf("[STUB] AccessGet(accessor=%s, target=%s)", accessorID, targetID) return nil, nil } func AccessWrite(records ...*Access) error { log.Printf("[STUB] AccessWrite(n=%d)", len(records)) return nil } func AccessRemove(accessorID, targetID string) error { log.Printf("[STUB] AccessRemove(accessor=%s, target=%s)", accessorID, targetID) return nil } func AccessList(f *AccessFilter) ([]*Access, error) { if f == nil { return nil, nil } var grants []*Access if f.AccessorID != "" { return grants, dbQuery( "SELECT AccessID, DossierID, GranteeID, EntryID, Relation, Ops, CreatedAt FROM access WHERE GranteeID = ?", []any{f.AccessorID}, &grants, ) } if f.TargetID != "" { return grants, dbQuery( "SELECT AccessID, DossierID, GranteeID, EntryID, Relation, Ops, CreatedAt FROM access WHERE DossierID = ?", []any{f.TargetID}, &grants, ) } return nil, nil } func AccessListByAccessor(granteeID string) ([]*Access, error) { log.Printf("[STUB] AccessListByAccessor(grantee=%s)", granteeID) return nil, nil } func AccessListByTargetWithNames(targetID string) ([]map[string]interface{}, error) { log.Printf("[STUB] AccessListByTargetWithNames(target=%s)", targetID) return nil, nil } func AccessUpdateTimestamp(granteeID, dossierID string) error { log.Printf("[STUB] AccessUpdateTimestamp(grantee=%s, dossier=%s)", granteeID, dossierID) return nil } func AccessGrantRole(dossierID, granteeID, role string) error { log.Printf("[STUB] AccessGrantRole(dossier=%s, grantee=%s, role=%s)", dossierID, granteeID, role) return nil } // --- Audit stubs --- func auditWrite(a *AuditEntry) { if a.AuditID == "" { a.AuditID = NewID() } if a.Timestamp == 0 { a.Timestamp = nowUnix() } db.Exec(`INSERT INTO audit (AuditID, Actor1ID, Actor2ID, TargetID, Action, Details, RelationID, Timestamp) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, a.AuditID, a.Actor1ID, a.Actor2ID, a.TargetID, Pack([]byte(a.Action)), Pack([]byte(a.Details)), a.RelationID, a.Timestamp, ) } func AuditAdd(a *AuditEntry) error { auditWrite(a) return nil } func AuditLog(actor1ID string, action string, targetID string, details string) { auditWrite(&AuditEntry{Actor1ID: actor1ID, Action: action, TargetID: targetID, Details: details}) } func AuditLogFull(actor1ID, actor2ID, targetID string, action, details string, relationID int) { auditWrite(&AuditEntry{Actor1ID: actor1ID, Actor2ID: actor2ID, TargetID: targetID, Action: action, Details: details, RelationID: relationID}) } func AuditQueryByActor(actor1ID string, from, to int64) ([]*AuditEntry, error) { log.Printf("[STUB] AuditQueryByActor(actor=%s)", actor1ID) return nil, nil } func AuditQueryByTarget(targetID string, from, to int64) ([]*AuditEntry, error) { log.Printf("[STUB] AuditQueryByTarget(target=%s)", targetID) return nil, nil } // --- Object stubs --- func ObjectWrite(ctx *AccessContext, dossierID, entryID string, data []byte) error { log.Printf("[STUB] ObjectWrite(dossier=%s, entry=%s, size=%d)", dossierID, entryID, len(data)) path := ObjectPath(dossierID, entryID) if err := os.MkdirAll(path[:len(path)-len(entryID)-1], 0750); err != nil { return err } return os.WriteFile(path, Pack(data), 0640) } func ObjectRead(ctx *AccessContext, dossierID, entryID string) ([]byte, error) { log.Printf("[STUB] ObjectRead(dossier=%s, entry=%s)", dossierID, entryID) packed, err := os.ReadFile(ObjectPath(dossierID, entryID)) if err != nil { return nil, err } return Unpack(packed), nil } func ObjectRemoveByDossier(ctx *AccessContext, dossierID string) error { log.Printf("[STUB] ObjectRemoveByDossier(dossier=%s)", dossierID) return os.RemoveAll(ObjectPath(dossierID, "")[:len(ObjectDir)+1+len(dossierID)]) } // --- Lab stubs --- func LabEntryListForIndex() ([]*Entry, error) { log.Printf("[STUB] LabEntryListForIndex") return nil, nil } func LabTestSave(t *LabTest) error { log.Printf("[STUB] LabTestSave") return nil } func LabRefSaveBatch(refs []LabReference) error { log.Printf("[STUB] LabRefSaveBatch(n=%d)", len(refs)) return nil } func RefDelete(table, col, id string) error { log.Printf("[STUB] RefDelete(table=%s, col=%s, id=%s)", table, col, id) return nil } // --- Genome types and stubs --- type GenomeMatch struct { RSID string `json:"rsid"` Genotype string `json:"genotype"` Gene string `json:"gene,omitempty"` Magnitude *float64 `json:"magnitude,omitempty"` Repute string `json:"repute,omitempty"` Summary string `json:"summary,omitempty"` Categories []string `json:"categories,omitempty"` } type GenomeQueryResult struct { Matches []GenomeMatch `json:"matches"` Returned int `json:"returned"` Total int `json:"total"` } type GenomeQueryOpts struct { Category string Search string Gene string RSIDs []string MinMagnitude float64 Repute string IncludeHidden bool Sort string Offset int Limit int AccessorID string } type ImageOpts struct { WC, WW float64 Zoom float64 PanX, PanY float64 } func GenomeQuery(ctx *AccessContext, dossierID string, opts GenomeQueryOpts) (*GenomeQueryResult, error) { log.Printf("[STUB] GenomeQuery(dossier=%s)", dossierID) return &GenomeQueryResult{}, nil } func ImageGet(ctx *AccessContext, id string, opts *ImageOpts) ([]byte, error) { log.Printf("[STUB] ImageGet(id=%s)", id) return ObjectRead(ctx, "", id) } func EntryCategoryCounts(ctx *AccessContext, dossierID string) (map[string]int, error) { log.Printf("[STUB] EntryCategoryCounts(dossier=%s)", dossierID) return map[string]int{}, nil } func EntryQueryByDate(dossierID string, from, to int64) ([]*Entry, error) { return EntryRead("", dossierID, &Filter{Category: -1, FromDate: from, ToDate: to}) } // --- Audit types and stubs --- type AuditFilter struct { ActorID string TargetID string Action string FromDate int64 ToDate int64 Limit int } func AuditList(f *AuditFilter) ([]*AuditEntry, error) { if f == nil { return nil, nil } q := "SELECT AuditID, Actor1ID, Actor2ID, TargetID, Action, Details, RelationID, Timestamp FROM audit WHERE 1=1" var args []any if f.TargetID != "" { q += " AND TargetID = ?" args = append(args, f.TargetID) } if f.ActorID != "" { q += " AND Actor1ID = ?" args = append(args, f.ActorID) } if f.FromDate > 0 { q += " AND Timestamp >= ?" args = append(args, f.FromDate) } if f.ToDate > 0 { q += " AND Timestamp < ?" args = append(args, f.ToDate) } q += " ORDER BY Timestamp DESC" if f.Limit > 0 { q += fmt.Sprintf(" LIMIT %d", f.Limit) } var entries []*AuditEntry err := dbQuery(q, args, &entries) return entries, err } // --- Tracker types and stubs --- type TrackerFilter struct { DossierID string Category string Type string ActiveOnly bool Limit int } func TrackerList(f *TrackerFilter) ([]*Tracker, error) { log.Printf("[STUB] TrackerList") return nil, nil } // --- Misc stubs --- func OpenReadOnly(path string) (*sql.DB, error) { log.Printf("[STUB] OpenReadOnly(path=%s)", path) return sql.Open("sqlite3", path+"?mode=ro") } func TrackerDistinctTypes(dossierID string) (map[string][]string, error) { log.Printf("[STUB] TrackerDistinctTypes(dossier=%s)", dossierID) return nil, nil } func MigrateCategory() error { log.Printf("[STUB] MigrateCategory") return nil } func MigrateDOB() error { log.Printf("[STUB] MigrateDOB") return nil }