Store repository source remotes
Authored by
mfwolffe <wolffemf@dukes.jmu.edu>
- SHA
0b94e811729595dd6c1ebf68dc69ef8b60ed9e7e- Parents
-
21eebc4 - Tree
8a14295
0b94e81
0b94e811729595dd6c1ebf68dc69ef8b60ed9e7e21eebc4
8a14295internal/actions/sqlc/models.gomodified@@ -1990,6 +1990,15 @@ type RepoRedirect struct { | ||
| 1990 | 1990 | RedirectedAt pgtype.Timestamptz |
| 1991 | 1991 | } |
| 1992 | 1992 | |
| 1993 | +type RepoSourceRemote struct { | |
| 1994 | + RepoID int64 | |
| 1995 | + RemoteUrl string | |
| 1996 | + LastFetchedAt pgtype.Timestamptz | |
| 1997 | + LastError pgtype.Text | |
| 1998 | + CreatedAt pgtype.Timestamptz | |
| 1999 | + UpdatedAt pgtype.Timestamptz | |
| 2000 | +} | |
| 2001 | + | |
| 1993 | 2002 | type RepoTopic struct { |
| 1994 | 2003 | RepoID int64 |
| 1995 | 2004 | Topic string |
internal/admin/sqlc/models.gomodified@@ -1990,6 +1990,15 @@ type RepoRedirect struct { | ||
| 1990 | 1990 | RedirectedAt pgtype.Timestamptz |
| 1991 | 1991 | } |
| 1992 | 1992 | |
| 1993 | +type RepoSourceRemote struct { | |
| 1994 | + RepoID int64 | |
| 1995 | + RemoteUrl string | |
| 1996 | + LastFetchedAt pgtype.Timestamptz | |
| 1997 | + LastError pgtype.Text | |
| 1998 | + CreatedAt pgtype.Timestamptz | |
| 1999 | + UpdatedAt pgtype.Timestamptz | |
| 2000 | +} | |
| 2001 | + | |
| 1993 | 2002 | type RepoTopic struct { |
| 1994 | 2003 | RepoID int64 |
| 1995 | 2004 | Topic string |
internal/auth/policy/sqlc/models.gomodified@@ -1990,6 +1990,15 @@ type RepoRedirect struct { | ||
| 1990 | 1990 | RedirectedAt pgtype.Timestamptz |
| 1991 | 1991 | } |
| 1992 | 1992 | |
| 1993 | +type RepoSourceRemote struct { | |
| 1994 | + RepoID int64 | |
| 1995 | + RemoteUrl string | |
| 1996 | + LastFetchedAt pgtype.Timestamptz | |
| 1997 | + LastError pgtype.Text | |
| 1998 | + CreatedAt pgtype.Timestamptz | |
| 1999 | + UpdatedAt pgtype.Timestamptz | |
| 2000 | +} | |
| 2001 | + | |
| 1993 | 2002 | type RepoTopic struct { |
| 1994 | 2003 | RepoID int64 |
| 1995 | 2004 | Topic string |
internal/checks/sqlc/models.gomodified@@ -1990,6 +1990,15 @@ type RepoRedirect struct { | ||
| 1990 | 1990 | RedirectedAt pgtype.Timestamptz |
| 1991 | 1991 | } |
| 1992 | 1992 | |
| 1993 | +type RepoSourceRemote struct { | |
| 1994 | + RepoID int64 | |
| 1995 | + RemoteUrl string | |
| 1996 | + LastFetchedAt pgtype.Timestamptz | |
| 1997 | + LastError pgtype.Text | |
| 1998 | + CreatedAt pgtype.Timestamptz | |
| 1999 | + UpdatedAt pgtype.Timestamptz | |
| 2000 | +} | |
| 2001 | + | |
| 1993 | 2002 | type RepoTopic struct { |
| 1994 | 2003 | RepoID int64 |
| 1995 | 2004 | Topic string |
internal/issues/sqlc/models.gomodified@@ -1990,6 +1990,15 @@ type RepoRedirect struct { | ||
| 1990 | 1990 | RedirectedAt pgtype.Timestamptz |
| 1991 | 1991 | } |
| 1992 | 1992 | |
| 1993 | +type RepoSourceRemote struct { | |
| 1994 | + RepoID int64 | |
| 1995 | + RemoteUrl string | |
| 1996 | + LastFetchedAt pgtype.Timestamptz | |
| 1997 | + LastError pgtype.Text | |
| 1998 | + CreatedAt pgtype.Timestamptz | |
| 1999 | + UpdatedAt pgtype.Timestamptz | |
| 2000 | +} | |
| 2001 | + | |
| 1993 | 2002 | type RepoTopic struct { |
| 1994 | 2003 | RepoID int64 |
| 1995 | 2004 | Topic string |
internal/meta/sqlc/models.gomodified@@ -1990,6 +1990,15 @@ type RepoRedirect struct { | ||
| 1990 | 1990 | RedirectedAt pgtype.Timestamptz |
| 1991 | 1991 | } |
| 1992 | 1992 | |
| 1993 | +type RepoSourceRemote struct { | |
| 1994 | + RepoID int64 | |
| 1995 | + RemoteUrl string | |
| 1996 | + LastFetchedAt pgtype.Timestamptz | |
| 1997 | + LastError pgtype.Text | |
| 1998 | + CreatedAt pgtype.Timestamptz | |
| 1999 | + UpdatedAt pgtype.Timestamptz | |
| 2000 | +} | |
| 2001 | + | |
| 1993 | 2002 | type RepoTopic struct { |
| 1994 | 2003 | RepoID int64 |
| 1995 | 2004 | Topic string |
internal/migrationsfs/migrations/0051_repo_source_remotes.sqladded@@ -0,0 +1,16 @@ | ||
| 1 | +-- SPDX-License-Identifier: AGPL-3.0-or-later | |
| 2 | + | |
| 3 | +CREATE TABLE repo_source_remotes ( | |
| 4 | + repo_id bigint PRIMARY KEY REFERENCES repos(id) ON DELETE CASCADE, | |
| 5 | + remote_url text NOT NULL, | |
| 6 | + last_fetched_at timestamptz, | |
| 7 | + last_error text, | |
| 8 | + created_at timestamptz NOT NULL DEFAULT now(), | |
| 9 | + updated_at timestamptz NOT NULL DEFAULT now(), | |
| 10 | + CHECK (remote_url <> ''), | |
| 11 | + CHECK (length(remote_url) <= 2048) | |
| 12 | +); | |
| 13 | + | |
| 14 | +CREATE TRIGGER repo_source_remotes_set_updated_at | |
| 15 | +BEFORE UPDATE ON repo_source_remotes | |
| 16 | +FOR EACH ROW EXECUTE FUNCTION tg_set_updated_at(); | |
internal/notif/sqlc/models.gomodified@@ -1990,6 +1990,15 @@ type RepoRedirect struct { | ||
| 1990 | 1990 | RedirectedAt pgtype.Timestamptz |
| 1991 | 1991 | } |
| 1992 | 1992 | |
| 1993 | +type RepoSourceRemote struct { | |
| 1994 | + RepoID int64 | |
| 1995 | + RemoteUrl string | |
| 1996 | + LastFetchedAt pgtype.Timestamptz | |
| 1997 | + LastError pgtype.Text | |
| 1998 | + CreatedAt pgtype.Timestamptz | |
| 1999 | + UpdatedAt pgtype.Timestamptz | |
| 2000 | +} | |
| 2001 | + | |
| 1993 | 2002 | type RepoTopic struct { |
| 1994 | 2003 | RepoID int64 |
| 1995 | 2004 | Topic string |
internal/orgs/sqlc/models.gomodified@@ -1990,6 +1990,15 @@ type RepoRedirect struct { | ||
| 1990 | 1990 | RedirectedAt pgtype.Timestamptz |
| 1991 | 1991 | } |
| 1992 | 1992 | |
| 1993 | +type RepoSourceRemote struct { | |
| 1994 | + RepoID int64 | |
| 1995 | + RemoteUrl string | |
| 1996 | + LastFetchedAt pgtype.Timestamptz | |
| 1997 | + LastError pgtype.Text | |
| 1998 | + CreatedAt pgtype.Timestamptz | |
| 1999 | + UpdatedAt pgtype.Timestamptz | |
| 2000 | +} | |
| 2001 | + | |
| 1993 | 2002 | type RepoTopic struct { |
| 1994 | 2003 | RepoID int64 |
| 1995 | 2004 | Topic string |
internal/pulls/sqlc/models.gomodified@@ -1990,6 +1990,15 @@ type RepoRedirect struct { | ||
| 1990 | 1990 | RedirectedAt pgtype.Timestamptz |
| 1991 | 1991 | } |
| 1992 | 1992 | |
| 1993 | +type RepoSourceRemote struct { | |
| 1994 | + RepoID int64 | |
| 1995 | + RemoteUrl string | |
| 1996 | + LastFetchedAt pgtype.Timestamptz | |
| 1997 | + LastError pgtype.Text | |
| 1998 | + CreatedAt pgtype.Timestamptz | |
| 1999 | + UpdatedAt pgtype.Timestamptz | |
| 2000 | +} | |
| 2001 | + | |
| 1993 | 2002 | type RepoTopic struct { |
| 1994 | 2003 | RepoID int64 |
| 1995 | 2004 | Topic string |
internal/ratelimit/sqlc/models.gomodified@@ -1990,6 +1990,15 @@ type RepoRedirect struct { | ||
| 1990 | 1990 | RedirectedAt pgtype.Timestamptz |
| 1991 | 1991 | } |
| 1992 | 1992 | |
| 1993 | +type RepoSourceRemote struct { | |
| 1994 | + RepoID int64 | |
| 1995 | + RemoteUrl string | |
| 1996 | + LastFetchedAt pgtype.Timestamptz | |
| 1997 | + LastError pgtype.Text | |
| 1998 | + CreatedAt pgtype.Timestamptz | |
| 1999 | + UpdatedAt pgtype.Timestamptz | |
| 2000 | +} | |
| 2001 | + | |
| 1993 | 2002 | type RepoTopic struct { |
| 1994 | 2003 | RepoID int64 |
| 1995 | 2004 | Topic string |
internal/repos/queries/source_remotes.sqladded@@ -0,0 +1,32 @@ | ||
| 1 | +-- SPDX-License-Identifier: AGPL-3.0-or-later | |
| 2 | + | |
| 3 | +-- name: GetRepoSourceRemote :one | |
| 4 | +SELECT repo_id, remote_url, last_fetched_at, last_error, created_at, updated_at | |
| 5 | +FROM repo_source_remotes | |
| 6 | +WHERE repo_id = $1; | |
| 7 | + | |
| 8 | +-- name: UpsertRepoSourceRemote :one | |
| 9 | +INSERT INTO repo_source_remotes (repo_id, remote_url, last_error) | |
| 10 | +VALUES ($1, $2, NULL) | |
| 11 | +ON CONFLICT (repo_id) DO UPDATE | |
| 12 | + SET remote_url = EXCLUDED.remote_url, | |
| 13 | + last_error = NULL, | |
| 14 | + updated_at = now() | |
| 15 | +RETURNING repo_id, remote_url, last_fetched_at, last_error, created_at, updated_at; | |
| 16 | + | |
| 17 | +-- name: DeleteRepoSourceRemote :exec | |
| 18 | +DELETE FROM repo_source_remotes | |
| 19 | +WHERE repo_id = $1; | |
| 20 | + | |
| 21 | +-- name: MarkRepoSourceRemoteFetched :exec | |
| 22 | +UPDATE repo_source_remotes | |
| 23 | + SET last_fetched_at = now(), | |
| 24 | + last_error = NULL, | |
| 25 | + updated_at = now() | |
| 26 | + WHERE repo_id = $1; | |
| 27 | + | |
| 28 | +-- name: MarkRepoSourceRemoteFetchError :exec | |
| 29 | +UPDATE repo_source_remotes | |
| 30 | + SET last_error = $2, | |
| 31 | + updated_at = now() | |
| 32 | + WHERE repo_id = $1; | |
internal/repos/source_remote.goadded@@ -0,0 +1,69 @@ | ||
| 1 | +// SPDX-License-Identifier: AGPL-3.0-or-later | |
| 2 | + | |
| 3 | +package repos | |
| 4 | + | |
| 5 | +import ( | |
| 6 | + "context" | |
| 7 | + "errors" | |
| 8 | + "fmt" | |
| 9 | + "net/url" | |
| 10 | + "strings" | |
| 11 | + | |
| 12 | + "github.com/tenseleyFlow/shithub/internal/security/ssrf" | |
| 13 | +) | |
| 14 | + | |
| 15 | +const MaxSourceRemoteURLLen = 2048 | |
| 16 | + | |
| 17 | +var ErrInvalidSourceRemote = errors.New("repos: invalid source remote URL") | |
| 18 | + | |
| 19 | +// NormalizeSourceRemoteURL validates and canonicalizes the public Git | |
| 20 | +// remote URL shithub is allowed to fetch from for source imports and | |
| 21 | +// submodule commit backfills. Credentials are deliberately not allowed | |
| 22 | +// here; private import credentials need a separate secret-backed design. | |
| 23 | +func NormalizeSourceRemoteURL(raw string) (string, error) { | |
| 24 | + raw = strings.TrimSpace(raw) | |
| 25 | + if raw == "" { | |
| 26 | + return "", nil | |
| 27 | + } | |
| 28 | + if len(raw) > MaxSourceRemoteURLLen { | |
| 29 | + return "", fmt.Errorf("%w: too long", ErrInvalidSourceRemote) | |
| 30 | + } | |
| 31 | + u, err := url.Parse(raw) | |
| 32 | + if err != nil { | |
| 33 | + return "", fmt.Errorf("%w: malformed URL", ErrInvalidSourceRemote) | |
| 34 | + } | |
| 35 | + switch strings.ToLower(u.Scheme) { | |
| 36 | + case "http", "https": | |
| 37 | + default: | |
| 38 | + return "", fmt.Errorf("%w: source imports currently support http(s) git remotes", ErrInvalidSourceRemote) | |
| 39 | + } | |
| 40 | + if u.Hostname() == "" { | |
| 41 | + return "", fmt.Errorf("%w: missing host", ErrInvalidSourceRemote) | |
| 42 | + } | |
| 43 | + if u.User != nil { | |
| 44 | + return "", fmt.Errorf("%w: credentials are not supported in source remote URLs", ErrInvalidSourceRemote) | |
| 45 | + } | |
| 46 | + if u.RawQuery != "" || u.Fragment != "" { | |
| 47 | + return "", fmt.Errorf("%w: query strings and fragments are not supported", ErrInvalidSourceRemote) | |
| 48 | + } | |
| 49 | + if strings.Trim(u.EscapedPath(), "/") == "" { | |
| 50 | + return "", fmt.Errorf("%w: missing repository path", ErrInvalidSourceRemote) | |
| 51 | + } | |
| 52 | + u.Scheme = strings.ToLower(u.Scheme) | |
| 53 | + u.Host = strings.ToLower(u.Host) | |
| 54 | + return u.String(), nil | |
| 55 | +} | |
| 56 | + | |
| 57 | +// ValidateSourceRemoteURL runs the same SSRF defenses used for webhooks | |
| 58 | +// before a URL is persisted or fetched by git. Git still receives the URL | |
| 59 | +// as argv (never through a shell), and fetch disables submodule recursion. | |
| 60 | +func ValidateSourceRemoteURL(ctx context.Context, raw string) (string, error) { | |
| 61 | + normalized, err := NormalizeSourceRemoteURL(raw) | |
| 62 | + if err != nil || normalized == "" { | |
| 63 | + return normalized, err | |
| 64 | + } | |
| 65 | + if err := ssrf.Default().ValidateWithResolve(ctx, normalized); err != nil { | |
| 66 | + return "", fmt.Errorf("%w: %v", ErrInvalidSourceRemote, err) | |
| 67 | + } | |
| 68 | + return normalized, nil | |
| 69 | +} | |
internal/repos/source_remote_test.goadded@@ -0,0 +1,58 @@ | ||
| 1 | +// SPDX-License-Identifier: AGPL-3.0-or-later | |
| 2 | + | |
| 3 | +package repos_test | |
| 4 | + | |
| 5 | +import ( | |
| 6 | + "errors" | |
| 7 | + "strings" | |
| 8 | + "testing" | |
| 9 | + | |
| 10 | + "github.com/tenseleyFlow/shithub/internal/repos" | |
| 11 | +) | |
| 12 | + | |
| 13 | +func TestNormalizeSourceRemoteURL(t *testing.T) { | |
| 14 | + t.Parallel() | |
| 15 | + for _, tt := range []struct { | |
| 16 | + name string | |
| 17 | + raw string | |
| 18 | + want string | |
| 19 | + }{ | |
| 20 | + {name: "https", raw: " https://github.com/FortranGoingOnForty/fgof-process.git ", want: "https://github.com/FortranGoingOnForty/fgof-process.git"}, | |
| 21 | + {name: "http", raw: "HTTP://git.example.com/owner/repo.git", want: "http://git.example.com/owner/repo.git"}, | |
| 22 | + {name: "empty clears", raw: " ", want: ""}, | |
| 23 | + } { | |
| 24 | + tt := tt | |
| 25 | + t.Run(tt.name, func(t *testing.T) { | |
| 26 | + t.Parallel() | |
| 27 | + got, err := repos.NormalizeSourceRemoteURL(tt.raw) | |
| 28 | + if err != nil { | |
| 29 | + t.Fatalf("NormalizeSourceRemoteURL(%q): %v", tt.raw, err) | |
| 30 | + } | |
| 31 | + if got != tt.want { | |
| 32 | + t.Fatalf("NormalizeSourceRemoteURL(%q) = %q, want %q", tt.raw, got, tt.want) | |
| 33 | + } | |
| 34 | + }) | |
| 35 | + } | |
| 36 | +} | |
| 37 | + | |
| 38 | +func TestNormalizeSourceRemoteURLRejectsUnsafeShapes(t *testing.T) { | |
| 39 | + t.Parallel() | |
| 40 | + for _, raw := range []string{ | |
| 41 | + "git@github.com:owner/repo.git", | |
| 42 | + "ssh://git@github.com/owner/repo.git", | |
| 43 | + "file:///tmp/repo.git", | |
| 44 | + "https://user:pass@example.com/owner/repo.git", | |
| 45 | + "https://example.com", | |
| 46 | + "https://example.com/owner/repo.git?token=secret", | |
| 47 | + "https://example.com/owner/repo.git#main", | |
| 48 | + strings.Repeat("a", repos.MaxSourceRemoteURLLen+1), | |
| 49 | + } { | |
| 50 | + raw := raw | |
| 51 | + t.Run(raw, func(t *testing.T) { | |
| 52 | + t.Parallel() | |
| 53 | + if got, err := repos.NormalizeSourceRemoteURL(raw); !errors.Is(err, repos.ErrInvalidSourceRemote) { | |
| 54 | + t.Fatalf("NormalizeSourceRemoteURL(%q) = %q, %v; want ErrInvalidSourceRemote", raw, got, err) | |
| 55 | + } | |
| 56 | + }) | |
| 57 | + } | |
| 58 | +} | |
internal/repos/sqlc/models.gomodified@@ -1990,6 +1990,15 @@ type RepoRedirect struct { | ||
| 1990 | 1990 | RedirectedAt pgtype.Timestamptz |
| 1991 | 1991 | } |
| 1992 | 1992 | |
| 1993 | +type RepoSourceRemote struct { | |
| 1994 | + RepoID int64 | |
| 1995 | + RemoteUrl string | |
| 1996 | + LastFetchedAt pgtype.Timestamptz | |
| 1997 | + LastError pgtype.Text | |
| 1998 | + CreatedAt pgtype.Timestamptz | |
| 1999 | + UpdatedAt pgtype.Timestamptz | |
| 2000 | +} | |
| 2001 | + | |
| 1993 | 2002 | type RepoTopic struct { |
| 1994 | 2003 | RepoID int64 |
| 1995 | 2004 | Topic string |
internal/repos/sqlc/querier.gomodified@@ -45,6 +45,7 @@ type Querier interface { | ||
| 45 | 45 | // (they would dangle once the repos row is gone; the FK ON DELETE |
| 46 | 46 | // CASCADE would handle it, but explicit is auditable). |
| 47 | 47 | DeleteRedirectsForRepo(ctx context.Context, db DBTX, repoID int64) error |
| 48 | + DeleteRepoSourceRemote(ctx context.Context, db DBTX, repoID int64) error | |
| 48 | 49 | ExistsRepoForOwnerOrg(ctx context.Context, db DBTX, arg ExistsRepoForOwnerOrgParams) (bool, error) |
| 49 | 50 | ExistsRepoForOwnerUser(ctx context.Context, db DBTX, arg ExistsRepoForOwnerUserParams) (bool, error) |
| 50 | 51 | // Called by the periodic worker (transfers:expire) — flips pending |
@@ -64,6 +65,8 @@ type Querier interface { | ||
| 64 | 65 | // jobs that need to derive the bare-repo on-disk path without round- |
| 65 | 66 | // tripping through the full user row. |
| 66 | 67 | GetRepoOwnerUsernameByID(ctx context.Context, db DBTX, id int64) (GetRepoOwnerUsernameByIDRow, error) |
| 68 | + // SPDX-License-Identifier: AGPL-3.0-or-later | |
| 69 | + GetRepoSourceRemote(ctx context.Context, db DBTX, repoID int64) (RepoSourceRemote, error) | |
| 67 | 70 | GetTransferRequest(ctx context.Context, db DBTX, id int64) (RepoTransferRequest, error) |
| 68 | 71 | HardDeleteRepo(ctx context.Context, db DBTX, id int64) error |
| 69 | 72 | InsertProfilePin(ctx context.Context, db DBTX, arg InsertProfilePinParams) error |
@@ -111,6 +114,8 @@ type Querier interface { | ||
| 111 | 114 | // Returns the current repo_id when (old_owner_user_id, old_name) hits |
| 112 | 115 | // a redirect row. |
| 113 | 116 | LookupRedirectByUserOwner(ctx context.Context, db DBTX, arg LookupRedirectByUserOwnerParams) (int64, error) |
| 117 | + MarkRepoSourceRemoteFetchError(ctx context.Context, db DBTX, arg MarkRepoSourceRemoteFetchErrorParams) error | |
| 118 | + MarkRepoSourceRemoteFetched(ctx context.Context, db DBTX, repoID int64) error | |
| 114 | 119 | // ─── fork-anchor cleanup on hard delete ──────────────────────────────── |
| 115 | 120 | // Children pointing at this repo lose their fork-of pointer. Mirrors |
| 116 | 121 | // GitHub's behavior when an upstream is deleted. |
@@ -171,6 +176,7 @@ type Querier interface { | ||
| 171 | 176 | UpsertBranchProtectionRule(ctx context.Context, db DBTX, arg UpsertBranchProtectionRuleParams) (int64, error) |
| 172 | 177 | UpsertProfilePinSetForOrg(ctx context.Context, db DBTX, ownerOrgID pgtype.Int8) (int64, error) |
| 173 | 178 | UpsertProfilePinSetForUser(ctx context.Context, db DBTX, ownerUserID pgtype.Int8) (int64, error) |
| 179 | + UpsertRepoSourceRemote(ctx context.Context, db DBTX, arg UpsertRepoSourceRemoteParams) (RepoSourceRemote, error) | |
| 174 | 180 | } |
| 175 | 181 | |
| 176 | 182 | var _ Querier = (*Queries)(nil) |
internal/repos/sqlc/source_remotes.sql.goadded@@ -0,0 +1,103 @@ | ||
| 1 | +// Code generated by sqlc. DO NOT EDIT. | |
| 2 | +// versions: | |
| 3 | +// sqlc v1.31.1 | |
| 4 | +// source: source_remotes.sql | |
| 5 | + | |
| 6 | +package reposdb | |
| 7 | + | |
| 8 | +import ( | |
| 9 | + "context" | |
| 10 | + | |
| 11 | + "github.com/jackc/pgx/v5/pgtype" | |
| 12 | +) | |
| 13 | + | |
| 14 | +const deleteRepoSourceRemote = `-- name: DeleteRepoSourceRemote :exec | |
| 15 | +DELETE FROM repo_source_remotes | |
| 16 | +WHERE repo_id = $1 | |
| 17 | +` | |
| 18 | + | |
| 19 | +func (q *Queries) DeleteRepoSourceRemote(ctx context.Context, db DBTX, repoID int64) error { | |
| 20 | + _, err := db.Exec(ctx, deleteRepoSourceRemote, repoID) | |
| 21 | + return err | |
| 22 | +} | |
| 23 | + | |
| 24 | +const getRepoSourceRemote = `-- name: GetRepoSourceRemote :one | |
| 25 | + | |
| 26 | +SELECT repo_id, remote_url, last_fetched_at, last_error, created_at, updated_at | |
| 27 | +FROM repo_source_remotes | |
| 28 | +WHERE repo_id = $1 | |
| 29 | +` | |
| 30 | + | |
| 31 | +// SPDX-License-Identifier: AGPL-3.0-or-later | |
| 32 | +func (q *Queries) GetRepoSourceRemote(ctx context.Context, db DBTX, repoID int64) (RepoSourceRemote, error) { | |
| 33 | + row := db.QueryRow(ctx, getRepoSourceRemote, repoID) | |
| 34 | + var i RepoSourceRemote | |
| 35 | + err := row.Scan( | |
| 36 | + &i.RepoID, | |
| 37 | + &i.RemoteUrl, | |
| 38 | + &i.LastFetchedAt, | |
| 39 | + &i.LastError, | |
| 40 | + &i.CreatedAt, | |
| 41 | + &i.UpdatedAt, | |
| 42 | + ) | |
| 43 | + return i, err | |
| 44 | +} | |
| 45 | + | |
| 46 | +const markRepoSourceRemoteFetchError = `-- name: MarkRepoSourceRemoteFetchError :exec | |
| 47 | +UPDATE repo_source_remotes | |
| 48 | + SET last_error = $2, | |
| 49 | + updated_at = now() | |
| 50 | + WHERE repo_id = $1 | |
| 51 | +` | |
| 52 | + | |
| 53 | +type MarkRepoSourceRemoteFetchErrorParams struct { | |
| 54 | + RepoID int64 | |
| 55 | + LastError pgtype.Text | |
| 56 | +} | |
| 57 | + | |
| 58 | +func (q *Queries) MarkRepoSourceRemoteFetchError(ctx context.Context, db DBTX, arg MarkRepoSourceRemoteFetchErrorParams) error { | |
| 59 | + _, err := db.Exec(ctx, markRepoSourceRemoteFetchError, arg.RepoID, arg.LastError) | |
| 60 | + return err | |
| 61 | +} | |
| 62 | + | |
| 63 | +const markRepoSourceRemoteFetched = `-- name: MarkRepoSourceRemoteFetched :exec | |
| 64 | +UPDATE repo_source_remotes | |
| 65 | + SET last_fetched_at = now(), | |
| 66 | + last_error = NULL, | |
| 67 | + updated_at = now() | |
| 68 | + WHERE repo_id = $1 | |
| 69 | +` | |
| 70 | + | |
| 71 | +func (q *Queries) MarkRepoSourceRemoteFetched(ctx context.Context, db DBTX, repoID int64) error { | |
| 72 | + _, err := db.Exec(ctx, markRepoSourceRemoteFetched, repoID) | |
| 73 | + return err | |
| 74 | +} | |
| 75 | + | |
| 76 | +const upsertRepoSourceRemote = `-- name: UpsertRepoSourceRemote :one | |
| 77 | +INSERT INTO repo_source_remotes (repo_id, remote_url, last_error) | |
| 78 | +VALUES ($1, $2, NULL) | |
| 79 | +ON CONFLICT (repo_id) DO UPDATE | |
| 80 | + SET remote_url = EXCLUDED.remote_url, | |
| 81 | + last_error = NULL, | |
| 82 | + updated_at = now() | |
| 83 | +RETURNING repo_id, remote_url, last_fetched_at, last_error, created_at, updated_at | |
| 84 | +` | |
| 85 | + | |
| 86 | +type UpsertRepoSourceRemoteParams struct { | |
| 87 | + RepoID int64 | |
| 88 | + RemoteUrl string | |
| 89 | +} | |
| 90 | + | |
| 91 | +func (q *Queries) UpsertRepoSourceRemote(ctx context.Context, db DBTX, arg UpsertRepoSourceRemoteParams) (RepoSourceRemote, error) { | |
| 92 | + row := db.QueryRow(ctx, upsertRepoSourceRemote, arg.RepoID, arg.RemoteUrl) | |
| 93 | + var i RepoSourceRemote | |
| 94 | + err := row.Scan( | |
| 95 | + &i.RepoID, | |
| 96 | + &i.RemoteUrl, | |
| 97 | + &i.LastFetchedAt, | |
| 98 | + &i.LastError, | |
| 99 | + &i.CreatedAt, | |
| 100 | + &i.UpdatedAt, | |
| 101 | + ) | |
| 102 | + return i, err | |
| 103 | +} | |
internal/users/sqlc/models.gomodified@@ -1990,6 +1990,15 @@ type RepoRedirect struct { | ||
| 1990 | 1990 | RedirectedAt pgtype.Timestamptz |
| 1991 | 1991 | } |
| 1992 | 1992 | |
| 1993 | +type RepoSourceRemote struct { | |
| 1994 | + RepoID int64 | |
| 1995 | + RemoteUrl string | |
| 1996 | + LastFetchedAt pgtype.Timestamptz | |
| 1997 | + LastError pgtype.Text | |
| 1998 | + CreatedAt pgtype.Timestamptz | |
| 1999 | + UpdatedAt pgtype.Timestamptz | |
| 2000 | +} | |
| 2001 | + | |
| 1993 | 2002 | type RepoTopic struct { |
| 1994 | 2003 | RepoID int64 |
| 1995 | 2004 | Topic string |
internal/webhook/sqlc/models.gomodified@@ -1990,6 +1990,15 @@ type RepoRedirect struct { | ||
| 1990 | 1990 | RedirectedAt pgtype.Timestamptz |
| 1991 | 1991 | } |
| 1992 | 1992 | |
| 1993 | +type RepoSourceRemote struct { | |
| 1994 | + RepoID int64 | |
| 1995 | + RemoteUrl string | |
| 1996 | + LastFetchedAt pgtype.Timestamptz | |
| 1997 | + LastError pgtype.Text | |
| 1998 | + CreatedAt pgtype.Timestamptz | |
| 1999 | + UpdatedAt pgtype.Timestamptz | |
| 2000 | +} | |
| 2001 | + | |
| 1993 | 2002 | type RepoTopic struct { |
| 1994 | 2003 | RepoID int64 |
| 1995 | 2004 | Topic string |
internal/worker/sqlc/models.gomodified@@ -1990,6 +1990,15 @@ type RepoRedirect struct { | ||
| 1990 | 1990 | RedirectedAt pgtype.Timestamptz |
| 1991 | 1991 | } |
| 1992 | 1992 | |
| 1993 | +type RepoSourceRemote struct { | |
| 1994 | + RepoID int64 | |
| 1995 | + RemoteUrl string | |
| 1996 | + LastFetchedAt pgtype.Timestamptz | |
| 1997 | + LastError pgtype.Text | |
| 1998 | + CreatedAt pgtype.Timestamptz | |
| 1999 | + UpdatedAt pgtype.Timestamptz | |
| 2000 | +} | |
| 2001 | + | |
| 1993 | 2002 | type RepoTopic struct { |
| 1994 | 2003 | RepoID int64 |
| 1995 | 2004 | Topic string |