Signed by hanna SSH key fingerprint: SHA256:4g9cWhkAAw8gwqhJUcVbSnGEvdGwklW+1Aa/fMUu59k
crates/store/src/bookmarks.rs +98 −0 crates/store/src/lib.rs +1 −0 crates/store/src/repos.rs +2 −2 data/repos/01/01kye754pv7cb136x910ppnvja.git/FETCH_HEAD +2 −0 data/repos/01/01kye754pv7cb136x910ppnvja.git/HEAD +1 −0 data/repos/01/01kye754pv7cb136x910ppnvja.git/config +9 −0 data/repos/01/01kye754pv7cb136x910ppnvja.git/description +1 −0 data/repos/01/01kye754pv7cb136x910ppnvja.git/hooks/README.sample +5 −0 data/repos/01/01kye754pv7cb136x910ppnvja.git/hooks/post-receive +2 −0 data/repos/01/01kye754pv7cb136x910ppnvja.git/hooks/pre-receive +2 −0 data/repos/01/01kye754pv7cb136x910ppnvja.git/info/exclude +2 −0 data/repos/01/01kye754pv7cb136x910ppnvja.git/logs/HEAD +1 −0 data/repos/01/01kye754pv7cb136x910ppnvja.git/logs/refs/heads/a +1 −0 data/repos/01/01kye754pv7cb136x910ppnvja.git/logs/refs/heads/main +1 −0 data/repos/01/01kye754pv7cb136x910ppnvja.git/objects/pack/pack-a7fdcb5c169f40c31c03119f33105da551053535.idx +0 −0 data/repos/01/01kye754pv7cb136x910ppnvja.git/objects/pack/pack-a7fdcb5c169f40c31c03119f33105da551053535.pack +0 −0 data/repos/01/01kye754pv7cb136x910ppnvja.git/objects/pack/pack-a7fdcb5c169f40c31c03119f33105da551053535.rev +0 −0 data/repos/01/01kye754pv7cb136x910ppnvja.git/refs/heads/a +1 −0 data/repos/01/01kye754pv7cb136x910ppnvja.git/refs/heads/main +1 −0 migrations/postgres/0018_bookmarks.sql +10 −0 migrations/sqlite/0018_bookmarks.sql +10 −0 crates/store/src/bookmarks.rs +98 −0 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + use model:: Repo ; 9 + use sqlx:: { AssertSqlSafe , Row } ; 10 + 11 + use crate :: repos:: REPO_COLUMNS ; 12 + use crate :: { Store , StoreError , new_id, now_ms} ; 13 + 14 + impl Store { 15 + 16 + 17 + 18 + 19 + 20 + pub async fn add_bookmark ( & self , user_id : & str , repo_id : & str ) -> Result < ( ) , StoreError > { 21 + sqlx:: query ( 22 + "INSERT INTO bookmarks (id, user_id, repo_id, created_at) VALUES ($1, $2, $3, $4) \ 23 + ON CONFLICT (user_id, repo_id) DO NOTHING" , 24 + ) 25 + . bind ( new_id ( ) ) 26 + . bind ( user_id) 27 + . bind ( repo_id) 28 + . bind ( now_ms ( ) ) 29 + . execute ( & self . pool) 30 + . await ?; 31 + Ok ( ( ) ) 32 + } 33 + 34 + 35 + 36 + 37 + 38 + 39 + pub async fn remove_bookmark ( & self , user_id : & str , repo_id : & str ) -> Result < ( ) , StoreError > { 40 + sqlx:: query ( "DELETE FROM bookmarks WHERE user_id = $1 AND repo_id = $2" ) 41 + . bind ( user_id) 42 + . bind ( repo_id) 43 + . execute ( & self . pool) 44 + . await ?; 45 + Ok ( ( ) ) 46 + } 47 + 48 + 49 + 50 + 51 + 52 + 53 + pub async fn is_bookmarked ( & self , user_id : & str , repo_id : & str ) -> Result < bool , StoreError > { 54 + let row = sqlx:: query ( "SELECT 1 AS n FROM bookmarks WHERE user_id = $1 AND repo_id = $2" ) 55 + . bind ( user_id) 56 + . bind ( repo_id) 57 + . fetch_optional ( & self . pool) 58 + . await ?; 59 + Ok ( row. is_some ( ) ) 60 + } 61 + 62 + 63 + 64 + 65 + 66 + 67 + pub async fn count_repo_bookmarks ( & self , repo_id : & str ) -> Result < i64 , StoreError > { 68 + let n: i64 = sqlx:: query ( "SELECT COUNT(*) AS n FROM bookmarks WHERE repo_id = $1" ) 69 + . bind ( repo_id) 70 + . fetch_one ( & self . pool) 71 + . await ? 72 + . try_get ( "n" ) ?; 73 + Ok ( n) 74 + } 75 + 76 + 77 + 78 + 79 + 80 + 81 + 82 + pub async fn bookmarked_repos ( & self , user_id : & str ) -> Result < Vec < Repo > , StoreError > { 83 + 84 + let cols = format ! ( "r.{}" , REPO_COLUMNS . replace ( ", " , ", r." ) ) ; 85 + let sql = format ! ( 86 + "SELECT {cols} FROM repos r JOIN bookmarks b ON b.repo_id = r.id \ 87 + WHERE b.user_id = $1 ORDER BY b.created_at DESC" 88 + ) ; 89 + let rows = sqlx:: query ( AssertSqlSafe ( sql) ) 90 + . bind ( user_id) 91 + . fetch_all ( & self . pool) 92 + . await ?; 93 + rows. iter ( ) 94 + . map ( |r| self . map_repo ( r) ) 95 + . collect :: < Result < _ , _ > > ( ) 96 + . map_err ( Into :: into) 97 + } 98 + }
crates/store/src/lib.rs +1 −0 Expand 25 hidden lines 26 26 27 27 28 28 mod admin; 29 + mod bookmarks; 29 30 mod groups; 30 31 mod invites; 31 32 mod issues;
crates/store/src/repos.rs +2 −2 Expand 10 hidden lines 11 11 use crate :: { Store , StoreError , map_conflict, new_id, now_ms} ; 12 12 13 13 14 - const REPO_COLUMNS : & str = "id, owner_id, group_id, name, path, description, visibility, \ 14 + pub ( crate ) const REPO_COLUMNS : & str = "id, owner_id, group_id, name, path, description, visibility, \ 15 15 default_branch, object_format, issues_enabled, pulls_enabled, releases_enabled, is_mirror, \ 16 16 fork_parent_id, next_iid, archived_at, size_bytes, pushed_at, created_at, updated_at" ; 17 17 Expand 465 hidden lines 483 483 } 484 484 485 485 486 - fn map_repo ( & self , row : & AnyRow ) -> Result < Repo , sqlx:: Error > { 486 + pub ( crate ) fn map_repo ( & self , row : & AnyRow ) -> Result < Repo , sqlx:: Error > { 487 487 Ok ( Repo { 488 488 id: row. try_get ( "id" ) ?, 489 489 owner_id: row. try_get ( "owner_id" ) ?,
data/repos/01/01kye754pv7cb136x910ppnvja.git/FETCH_HEAD +2 −0 1 + 66215cd4f32408a28a91cc10f0080bb4b3beb2a6 branch 'a' of data/repos/01/01kyc0j4dzrnx6afv3wwfvxgn4 2 + 94ffb4ef79a86c7e24d038233859149ba61439aa branch 'main' of data/repos/01/01kyc0j4dzrnx6afv3wwfvxgn4
data/repos/01/01kye754pv7cb136x910ppnvja.git/HEAD +1 −0 data/repos/01/01kye754pv7cb136x910ppnvja.git/config +9 −0 1 + [core] 2 + bare = true 3 + repositoryformatversion = 0 4 + filemode = true 5 + logallrefupdates = true 6 + [receive] 7 + denyNonFastForwards = false 8 + [gc] 9 + auto = 0
data/repos/01/01kye754pv7cb136x910ppnvja.git/description +1 −0 1 + Unnamed repository; edit this file 'description' to name the repository.
data/repos/01/01kye754pv7cb136x910ppnvja.git/hooks/README.sample +5 −0 data/repos/01/01kye754pv7cb136x910ppnvja.git/hooks/post-receive +2 −0 1 + 2 + exec "/nix/store/2ncl66xwbsl6qf2766w3sxy3dv9vaply-fabrica-0.1.0/bin/.fabrica-wrapped" hook post-receive
data/repos/01/01kye754pv7cb136x910ppnvja.git/hooks/pre-receive +2 −0 data/repos/01/01kye754pv7cb136x910ppnvja.git/info/exclude +2 −0 1 + # File patterns to ignore; see `git help ignore` for more information. 2 + # Lines that start with '#' are comments.
data/repos/01/01kye754pv7cb136x910ppnvja.git/logs/HEAD +1 −0 1 + 0000000000000000000000000000000000000000 94ffb4ef79a86c7e24d038233859149ba61439aa hanna <me@hanna.lol> 1785036182 -0400 fetch --prune --quiet data/repos/01/01kyc0j4dzrnx6afv3wwfvxgn4.git +refs/heads/*:refs/heads/* +refs/tags/*:refs/tags/*: storing head
data/repos/01/01kye754pv7cb136x910ppnvja.git/logs/refs/heads/a +1 −0 1 + 0000000000000000000000000000000000000000 66215cd4f32408a28a91cc10f0080bb4b3beb2a6 hanna <me@hanna.lol> 1785036182 -0400 fetch --prune --quiet data/repos/01/01kyc0j4dzrnx6afv3wwfvxgn4.git +refs/heads/*:refs/heads/* +refs/tags/*:refs/tags/*: storing head
data/repos/01/01kye754pv7cb136x910ppnvja.git/logs/refs/heads/main +1 −0 1 + 0000000000000000000000000000000000000000 94ffb4ef79a86c7e24d038233859149ba61439aa hanna <me@hanna.lol> 1785036182 -0400 fetch --prune --quiet data/repos/01/01kyc0j4dzrnx6afv3wwfvxgn4.git +refs/heads/*:refs/heads/* +refs/tags/*:refs/tags/*: storing head
data/repos/01/01kye754pv7cb136x910ppnvja.git/objects/pack/pack-a7fdcb5c169f40c31c03119f33105da551053535.idx +0 −0 Binary file not shown.
data/repos/01/01kye754pv7cb136x910ppnvja.git/objects/pack/pack-a7fdcb5c169f40c31c03119f33105da551053535.pack +0 −0 Binary file not shown.
data/repos/01/01kye754pv7cb136x910ppnvja.git/objects/pack/pack-a7fdcb5c169f40c31c03119f33105da551053535.rev +0 −0 Binary file not shown.
data/repos/01/01kye754pv7cb136x910ppnvja.git/refs/heads/a +1 −0 1 + 66215cd4f32408a28a91cc10f0080bb4b3beb2a6
data/repos/01/01kye754pv7cb136x910ppnvja.git/refs/heads/main +1 −0 1 + 94ffb4ef79a86c7e24d038233859149ba61439aa
migrations/postgres/0018_bookmarks.sql +10 −0 1 + -- Bookmarks (stars): a user marking a repository. Public on the user's profile, 2 + -- filtered by the viewer's read access when listed. 3 + CREATE TABLE bookmarks ( 4 + id TEXT PRIMARY KEY , 5 + user_id TEXT NOT NULL REFERENCES users ( id) ON DELETE CASCADE , 6 + repo_id TEXT NOT NULL REFERENCES repos ( id) ON DELETE CASCADE , 7 + created_at BIGINT NOT NULL 8 + ) ; 9 + CREATE UNIQUE INDEX idx_bookmarks_user_repo ON bookmarks ( user_id, repo_id) ; 10 + CREATE INDEX idx_bookmarks_repo ON bookmarks ( repo_id) ;
migrations/sqlite/0018_bookmarks.sql +10 −0 1 + -- Bookmarks (stars): a user marking a repository. Public on the user's profile, 2 + -- filtered by the viewer's read access when listed. 3 + CREATE TABLE bookmarks ( 4 + id TEXT PRIMARY KEY , 5 + user_id TEXT NOT NULL REFERENCES users ( id) ON DELETE CASCADE , 6 + repo_id TEXT NOT NULL REFERENCES repos ( id) ON DELETE CASCADE , 7 + created_at BIGINT NOT NULL 8 + ) ; 9 + CREATE UNIQUE INDEX idx_bookmarks_user_repo ON bookmarks ( user_id, repo_id) ; 10 + CREATE INDEX idx_bookmarks_repo ON bookmarks ( repo_id) ;