Signed by hanna SSH key fingerprint: SHA256:4g9cWhkAAw8gwqhJUcVbSnGEvdGwklW+1Aa/fMUu59k
crates/config/src/lib.rs +1 −0 crates/config/src/types.rs +3 −0 crates/store/src/lfs.rs +87 −0 crates/store/src/lib.rs +1 −0 fabrica.example.toml +1 −0 migrations/postgres/0013_lfs.sql +10 −0 migrations/sqlite/0013_lfs.sql +10 −0 crates/config/src/lib.rs +1 −0 Expand 182 hidden lines 183 183 184 184 check_dir_creatable ( "storage.data_dir" , & config. storage. data_dir, & mut errors) ; 185 185 check_dir_creatable ( "storage.repo_dir" , & config. storage. repo_dir, & mut errors) ; 186 + check_dir_creatable ( "storage.lfs_dir" , & config. storage. lfs_dir, & mut errors) ; 186 187 187 188 188 189 if config. mail. backend == MailBackend :: Smtp {
crates/config/src/types.rs +3 −0 Expand 277 hidden lines 278 278 pub data_dir: PathBuf , 279 279 280 280 pub repo_dir: PathBuf , 281 + 282 + pub lfs_dir: PathBuf , 281 283 } 282 284 283 285 impl Default for Storage { Expand 1 hidden lines 285 287 Self { 286 288 data_dir: PathBuf :: from ( "/var/lib/fabrica" ) , 287 289 repo_dir: PathBuf :: from ( "/var/lib/fabrica/repos" ) , 290 + lfs_dir: PathBuf :: from ( "/var/lib/fabrica/lfs" ) , 288 291 } 289 292 } 290 293 }
crates/store/src/lfs.rs +87 −0 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + use sqlx:: Row ; 11 + 12 + use crate :: { Store , StoreError , new_id, now_ms} ; 13 + 14 + impl Store { 15 + 16 + 17 + 18 + 19 + 20 + pub async fn lfs_object_exists ( & self , repo_id : & str , oid : & str ) -> Result < bool , StoreError > { 21 + let row = sqlx:: query ( "SELECT 1 AS n FROM lfs_objects WHERE repo_id = $1 AND oid = $2" ) 22 + . bind ( repo_id) 23 + . bind ( oid) 24 + . fetch_optional ( & self . pool) 25 + . await ?; 26 + Ok ( row. is_some ( ) ) 27 + } 28 + 29 + 30 + 31 + 32 + 33 + 34 + pub async fn lfs_object_size ( 35 + & self , 36 + repo_id : & str , 37 + oid : & str , 38 + ) -> Result < Option < i64 > , StoreError > { 39 + let row = sqlx:: query ( "SELECT size AS n FROM lfs_objects WHERE repo_id = $1 AND oid = $2" ) 40 + . bind ( repo_id) 41 + . bind ( oid) 42 + . fetch_optional ( & self . pool) 43 + . await ?; 44 + Ok ( row. map ( |r| r. try_get ( "n" ) ) . transpose ( ) ?) 45 + } 46 + 47 + 48 + 49 + 50 + 51 + 52 + 53 + pub async fn lfs_object_add ( 54 + & self , 55 + repo_id : & str , 56 + oid : & str , 57 + size : i64 , 58 + ) -> Result < ( ) , StoreError > { 59 + sqlx:: query ( 60 + "INSERT INTO lfs_objects (id, repo_id, oid, size, created_at) \ 61 + VALUES ($1, $2, $3, $4, $5) ON CONFLICT (repo_id, oid) DO NOTHING" , 62 + ) 63 + . bind ( new_id ( ) ) 64 + . bind ( repo_id) 65 + . bind ( oid) 66 + . bind ( size) 67 + . bind ( now_ms ( ) ) 68 + . execute ( & self . pool) 69 + . await ?; 70 + Ok ( ( ) ) 71 + } 72 + 73 + 74 + 75 + 76 + 77 + 78 + pub async fn lfs_repo_bytes ( & self , repo_id : & str ) -> Result < i64 , StoreError > { 79 + let n: i64 = 80 + sqlx:: query ( "SELECT COALESCE(SUM(size), 0) AS n FROM lfs_objects WHERE repo_id = $1" ) 81 + . bind ( repo_id) 82 + . fetch_one ( & self . pool) 83 + . await ? 84 + . try_get ( "n" ) ?; 85 + Ok ( n) 86 + } 87 + }
crates/store/src/lib.rs +1 −0 Expand 30 hidden lines 31 31 mod issues; 32 32 mod keys; 33 33 mod labels; 34 + mod lfs; 34 35 mod repos; 35 36 mod sessions; 36 37 mod tokens;
fabrica.example.toml +1 −0 Expand 40 hidden lines 41 41 [ storage ] 42 42 data_dir = "/var/lib/fabrica" 43 43 repo_dir = "/var/lib/fabrica/repos" 44 + lfs_dir = "/var/lib/fabrica/lfs" 44 45 45 46 [ database ] 46 47 url = "sqlite:///var/lib/fabrica/fabrica.db"
migrations/postgres/0013_lfs.sql +10 −0 1 + -- Git LFS: track which content-addressed objects (by sha256 oid) each repo has 2 + -- stored. The bytes live on disk under storage.lfs_dir, sharded by oid prefix. 3 + CREATE TABLE lfs_objects ( 4 + id TEXT PRIMARY KEY , 5 + repo_id TEXT NOT NULL REFERENCES repos ( id) ON DELETE CASCADE , 6 + oid TEXT NOT NULL , 7 + size BIGINT NOT NULL , 8 + created_at BIGINT NOT NULL 9 + ) ; 10 + CREATE UNIQUE INDEX idx_lfs_objects_repo_oid ON lfs_objects ( repo_id, oid) ;
migrations/sqlite/0013_lfs.sql +10 −0 1 + -- Git LFS: track which content-addressed objects (by sha256 oid) each repo has 2 + -- stored. The bytes live on disk under storage.lfs_dir, sharded by oid prefix. 3 + CREATE TABLE lfs_objects ( 4 + id TEXT PRIMARY KEY , 5 + repo_id TEXT NOT NULL REFERENCES repos ( id) ON DELETE CASCADE , 6 + oid TEXT NOT NULL , 7 + size BIGINT NOT NULL , 8 + created_at BIGINT NOT NULL 9 + ) ; 10 + CREATE UNIQUE INDEX idx_lfs_objects_repo_oid ON lfs_objects ( repo_id, oid) ;