package db import ( "context" "database/sql" "errors" "fmt" "strings" "git.faercol.me/faercol/polyculeconnect/polyculeconnect/config" "git.faercol.me/faercol/polyculeconnect/polyculeconnect/internal/model" _ "github.com/mattn/go-sqlite3" ) const clientRows = `"client"."id", "client"."secret", "client"."redirect_uris", "client"."trusted_peers", "client"."name"` type ClientDB interface { GetClientByID(ctx context.Context, id string) (*model.Client, error) } type sqlClientDB struct { db *sql.DB } func strArrayToSlice(rawVal string) []string { // TODO this won't work if there's more than one element res := []string{} insideStr, ok := strings.CutPrefix(rawVal, `["`) if !ok { return res } insideStr, ok = strings.CutSuffix(insideStr, `"]`) if !ok { return res } return []string{insideStr} } func clientFromRow(row *sql.Row) (*model.Client, error) { var res model.Client redirectURIsStr := "" trustedPeersStr := "" if err := row.Scan(&res.ID, &res.Secret, &redirectURIsStr, &trustedPeersStr, &res.Name); err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, ErrNotFound } return nil, fmt.Errorf("invalid format for client: %w", err) } res.ClientConfig.RedirectURIs = strArrayToSlice(redirectURIsStr) res.ClientConfig.TrustedPeers = strArrayToSlice(trustedPeersStr) return &res, nil } func (db *sqlClientDB) GetClientByID(ctx context.Context, id string) (*model.Client, error) { query := fmt.Sprintf(`SELECT %s FROM "client" WHERE "id" = ?`, clientRows) row := db.db.QueryRowContext(ctx, query, id) return clientFromRow(row) } func New(conf config.AppConfig) (*sqlClientDB, error) { db, err := sql.Open("sqlite3", conf.StorageConfig.File) if err != nil { return nil, fmt.Errorf("failed to open DB: %w", err) } return &sqlClientDB{db: db}, nil }