feat: tabla apps en registry — modelo, parser, indexer y CLI
Agrega soporte completo para indexar aplicaciones del directorio apps/. Cada app tiene un descriptor app.md con frontmatter YAML que el indexer recoge automaticamente. Incluye migracion 004, modelo App, ParseAppMD, ValidateApp, store CRUD con FTS5, y soporte en fn list/search/show. Crea descriptores app.md para docker_tui, pipeline_launcher y metabase_registry.
This commit is contained in:
+108
-2
@@ -261,12 +261,118 @@ func (db *DB) DeleteType(id string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Purge deletes all data from both tables. Used before re-indexing.
|
||||
// InsertApp inserts or replaces an app entry.
|
||||
func (db *DB) InsertApp(a *App) error {
|
||||
now := time.Now().UTC().Format(time.RFC3339)
|
||||
if a.CreatedAt.IsZero() {
|
||||
a.CreatedAt = time.Now().UTC()
|
||||
}
|
||||
a.UpdatedAt = time.Now().UTC()
|
||||
|
||||
if a.ID == "" {
|
||||
a.ID = GenerateID(a.Name, a.Lang, a.Domain)
|
||||
}
|
||||
|
||||
_, err := db.conn.Exec(`
|
||||
INSERT OR REPLACE INTO apps (
|
||||
id, name, lang, domain, description, tags,
|
||||
uses_functions, uses_types, framework, entry_point,
|
||||
documentation, notes, dir_path, created_at, updated_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
a.ID, a.Name, a.Lang, a.Domain, a.Description, marshalStrings(a.Tags),
|
||||
marshalStrings(a.UsesFunctions), marshalStrings(a.UsesTypes), a.Framework, a.EntryPoint,
|
||||
a.Documentation, a.Notes, a.DirPath, a.CreatedAt.Format(time.RFC3339), now,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetApp returns a single app by ID.
|
||||
func (db *DB) GetApp(id string) (*App, error) {
|
||||
rows, err := db.conn.Query("SELECT * FROM apps WHERE id = ?", id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
apps, err := scanApps(rows)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(apps) == 0 {
|
||||
return nil, fmt.Errorf("app %q not found", id)
|
||||
}
|
||||
return &apps[0], nil
|
||||
}
|
||||
|
||||
// SearchApps performs FTS search on apps with optional filters.
|
||||
func (db *DB) SearchApps(query string, lang, domain string) ([]App, error) {
|
||||
where := []string{}
|
||||
args := []any{}
|
||||
|
||||
if query != "" {
|
||||
where = append(where, "a.id IN (SELECT id FROM apps_fts WHERE apps_fts MATCH ?)")
|
||||
args = append(args, query)
|
||||
}
|
||||
if lang != "" {
|
||||
where = append(where, "a.lang = ?")
|
||||
args = append(args, lang)
|
||||
}
|
||||
if domain != "" {
|
||||
where = append(where, "a.domain = ?")
|
||||
args = append(args, domain)
|
||||
}
|
||||
|
||||
sql := "SELECT * FROM apps a"
|
||||
if len(where) > 0 {
|
||||
sql += " WHERE " + strings.Join(where, " AND ")
|
||||
}
|
||||
sql += " ORDER BY a.name"
|
||||
|
||||
rows, err := db.conn.Query(sql, args...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("search apps: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
return scanApps(rows)
|
||||
}
|
||||
|
||||
func scanApps(rows interface{ Next() bool; Scan(...any) error }) ([]App, error) {
|
||||
var result []App
|
||||
for rows.Next() {
|
||||
var a App
|
||||
var tagsJSON, usesFnJSON, usesTypJSON string
|
||||
var createdAt, updatedAt string
|
||||
|
||||
err := rows.Scan(
|
||||
&a.ID, &a.Name, &a.Lang, &a.Domain, &a.Description, &tagsJSON,
|
||||
&usesFnJSON, &usesTypJSON, &a.Framework, &a.EntryPoint,
|
||||
&a.Documentation, &a.Notes, &a.DirPath, &createdAt, &updatedAt,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("scanning app: %w", err)
|
||||
}
|
||||
|
||||
a.Tags = unmarshalStrings(tagsJSON)
|
||||
a.UsesFunctions = unmarshalStrings(usesFnJSON)
|
||||
a.UsesTypes = unmarshalStrings(usesTypJSON)
|
||||
a.CreatedAt, _ = time.Parse(time.RFC3339, createdAt)
|
||||
a.UpdatedAt, _ = time.Parse(time.RFC3339, updatedAt)
|
||||
|
||||
result = append(result, a)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Purge deletes all data from functions, types and apps. Used before re-indexing.
|
||||
func (db *DB) Purge() error {
|
||||
if _, err := db.conn.Exec("DELETE FROM functions"); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := db.conn.Exec("DELETE FROM types")
|
||||
if _, err := db.conn.Exec("DELETE FROM types"); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := db.conn.Exec("DELETE FROM apps")
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user