A vibe coded tangled fork which supports pijul.
at e620e86a00c541e18e9e8097f97e51962631c5cd 116 lines 2.6 kB view raw
1package db 2 3import ( 4 "context" 5 "database/sql" 6 "errors" 7 "fmt" 8 "log" 9 "strings" 10 11 "tangled.org/core/knotmirror/models" 12 "tangled.org/core/orm" 13) 14 15func UpsertHost(ctx context.Context, e *sql.DB, host *models.Host) error { 16 if _, err := e.ExecContext(ctx, 17 `insert into hosts (hostname, no_ssl, status, last_seq) 18 values (?, ?, ?, ?) 19 on conflict(hostname) do update set 20 no_ssl = excluded.no_ssl, 21 status = excluded.status, 22 last_seq = excluded.last_seq 23 `, 24 host.Hostname, 25 host.NoSSL, 26 host.Status, 27 host.LastSeq, 28 ); err != nil { 29 return fmt.Errorf("upserting host: %w", err) 30 } 31 return nil 32} 33 34func GetHost(ctx context.Context, e *sql.DB, hostname string) (*models.Host, error) { 35 var host models.Host 36 if err := e.QueryRowContext(ctx, 37 `select hostname, no_ssl, status, last_seq 38 from hosts where hostname = ?`, 39 hostname, 40 ).Scan( 41 &host.Hostname, 42 &host.NoSSL, 43 &host.Status, 44 &host.LastSeq, 45 ); err != nil { 46 if errors.Is(err, sql.ErrNoRows) { 47 return nil, nil 48 } 49 return nil, err 50 } 51 return &host, nil 52} 53 54func StoreCursors(ctx context.Context, e *sql.DB, cursors []models.HostCursor) error { 55 tx, err := e.BeginTx(ctx, nil) 56 if err != nil { 57 return fmt.Errorf("starting transaction: %w", err) 58 } 59 defer tx.Rollback() 60 for _, cur := range cursors { 61 if cur.LastSeq <= 0 { 62 continue 63 } 64 if _, err := tx.ExecContext(ctx, 65 `update hosts set last_seq = ? where hostname = ?`, 66 cur.LastSeq, 67 cur.Hostname, 68 ); err != nil { 69 log.Println("failed to persist host cursor", "host:", cur.Hostname, "lastSeq", cur.LastSeq) 70 } 71 } 72 return tx.Commit() 73} 74 75func ListHosts(ctx context.Context, e *sql.DB, filters ...orm.Filter) ([]models.Host, error) { 76 var conditions []string 77 var args []any 78 79 for _, filter := range filters { 80 conditions = append(conditions, filter.Condition()) 81 args = append(args, filter.Arg()...) 82 } 83 84 whereClause := "" 85 if len(conditions) > 0 { 86 whereClause = " where " + strings.Join(conditions, " and ") 87 } 88 89 rows, err := e.QueryContext(ctx, 90 `select hostname, no_ssl, status, last_seq 91 from hosts` + whereClause, 92 args..., 93 ) 94 if err != nil { 95 return nil, fmt.Errorf("querying hosts: %w", err) 96 } 97 defer rows.Close() 98 99 var hosts []models.Host 100 for rows.Next() { 101 var host models.Host 102 if err := rows.Scan( 103 &host.Hostname, 104 &host.NoSSL, 105 &host.Status, 106 &host.LastSeq, 107 ); err != nil { 108 return nil, fmt.Errorf("scanning row: %w", err) 109 } 110 hosts = append(hosts, host) 111 } 112 if err := rows.Err(); err != nil { 113 return nil, fmt.Errorf("scanning rows: %w ", err) 114 } 115 return hosts, nil 116}