A vibe coded tangled fork which supports pijul.
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}