@@ -6,7 +6,7 @@ use std::sync::Arc;
6
6
7
7
use crate :: auth:: jwt:: JwtHelper ;
8
8
use crate :: auth:: oauth:: providers:: { ConfiguredOAuthProviders , OAuthProviderType } ;
9
- use crate :: config:: proto:: { Config , QueryApiConfig , RecordApiConfig } ;
9
+ use crate :: config:: proto:: { Config , QueryApiConfig , RecordApiConfig , S3StorageConfig } ;
10
10
use crate :: config:: { validate_config, write_config_and_vault_textproto} ;
11
11
use crate :: constants:: SITE_URL_DEFAULT ;
12
12
use crate :: data_dir:: DataDir ;
@@ -36,8 +36,8 @@ struct InternalState {
36
36
jwt : JwtHelper ,
37
37
38
38
table_metadata : TableMetadataCache ,
39
+ object_store : Box < dyn ObjectStore + Send + Sync > ,
39
40
40
- #[ allow( unused) ]
41
41
runtime : RuntimeHandle ,
42
42
43
43
#[ cfg( test) ]
@@ -54,6 +54,7 @@ pub(crate) struct AppStateArgs {
54
54
pub conn : Connection ,
55
55
pub logs_conn : Connection ,
56
56
pub jwt : JwtHelper ,
57
+ pub object_store : Box < dyn ObjectStore + Send + Sync > ,
57
58
pub js_runtime_threads : Option < usize > ,
58
59
}
59
60
@@ -125,6 +126,7 @@ impl AppState {
125
126
logs_conn : args. logs_conn ,
126
127
jwt : args. jwt ,
127
128
table_metadata : args. table_metadata ,
129
+ object_store : args. object_store ,
128
130
runtime,
129
131
#[ cfg( test) ]
130
132
cleanup : vec ! [ ] ,
@@ -166,13 +168,8 @@ impl AppState {
166
168
self . table_metadata ( ) . invalidate_all ( ) . await
167
169
}
168
170
169
- pub ( crate ) fn objectstore (
170
- & self ,
171
- ) -> Result < Box < dyn ObjectStore + Send + Sync > , object_store:: Error > {
172
- // FIXME: We should probably have a long-lived store on AppState.
173
- return Ok ( Box :: new (
174
- object_store:: local:: LocalFileSystem :: new_with_prefix ( self . data_dir ( ) . uploads_path ( ) ) ?,
175
- ) ) ;
171
+ pub ( crate ) fn objectstore ( & self ) -> & ( dyn ObjectStore + Send + Sync ) {
172
+ return & * self . state . object_store ;
176
173
}
177
174
178
175
pub ( crate ) fn get_oauth_provider ( & self , name : & str ) -> Option < Arc < OAuthProviderType > > {
@@ -371,12 +368,32 @@ pub async fn test_state(options: Option<TestStateOptions>) -> anyhow::Result<App
371
368
let main_conn_clone1 = main_conn. clone ( ) ;
372
369
let table_metadata_clone = table_metadata. clone ( ) ;
373
370
371
+ let data_dir = DataDir ( temp_dir. path ( ) . to_path_buf ( ) ) ;
372
+
373
+ let object_store = if std:: env:: var ( "TEST_S3_OBJECT_STORE" ) . map_or ( false , |v| v == "TRUE" ) {
374
+ info ! ( "Use S3 Storage for tests" ) ;
375
+
376
+ build_objectstore (
377
+ & data_dir,
378
+ Some ( & S3StorageConfig {
379
+ endpoint : Some ( "http://127.0.0.1:9000" . to_string ( ) ) ,
380
+ region : None ,
381
+ bucket_name : Some ( "test" . to_string ( ) ) ,
382
+ access_key : Some ( "minioadmin" . to_string ( ) ) ,
383
+ secret_access_key : Some ( "minioadmin" . to_string ( ) ) ,
384
+ } ) ,
385
+ )
386
+ . unwrap ( )
387
+ } else {
388
+ build_objectstore ( & data_dir, None ) . unwrap ( )
389
+ } ;
390
+
374
391
let runtime = RuntimeHandle :: new ( ) ;
375
392
runtime. set_connection ( main_conn. clone ( ) ) ;
376
393
377
394
return Ok ( AppState {
378
395
state : Arc :: new ( InternalState {
379
- data_dir : DataDir ( temp_dir . path ( ) . to_path_buf ( ) ) ,
396
+ data_dir,
380
397
public_dir : None ,
381
398
dev : true ,
382
399
oauth : Computed :: new ( & config, |c| {
@@ -415,6 +432,7 @@ pub async fn test_state(options: Option<TestStateOptions>) -> anyhow::Result<App
415
432
logs_conn,
416
433
jwt : jwt:: test_jwt_helper ( ) ,
417
434
table_metadata,
435
+ object_store,
418
436
runtime,
419
437
cleanup : vec ! [ Box :: new( temp_dir) ] ,
420
438
} ) ,
@@ -445,3 +463,44 @@ fn build_query_api(conn: libsql::Connection, config: QueryApiConfig) -> Result<Q
445
463
// TODO: Check virtual table exists
446
464
return QueryApi :: from ( conn, config) ;
447
465
}
466
+
467
+ pub ( crate ) fn build_objectstore (
468
+ data_dir : & DataDir ,
469
+ config : Option < & S3StorageConfig > ,
470
+ ) -> Result < Box < dyn ObjectStore + Send + Sync > , object_store:: Error > {
471
+ if let Some ( config) = config {
472
+ let mut builder = object_store:: aws:: AmazonS3Builder :: from_env ( ) ;
473
+
474
+ if let Some ( ref endpoint) = config. endpoint {
475
+ builder = builder. with_endpoint ( endpoint) ;
476
+
477
+ if endpoint. starts_with ( "http://" ) {
478
+ builder =
479
+ builder. with_client_options ( object_store:: ClientOptions :: default ( ) . with_allow_http ( true ) )
480
+ }
481
+ }
482
+
483
+ if let Some ( ref region) = config. region {
484
+ builder = builder. with_region ( region) ;
485
+ }
486
+
487
+ let Some ( ref bucket_name) = config. bucket_name else {
488
+ panic ! ( "S3StorageConfig missing 'bucket_name'." ) ;
489
+ } ;
490
+ builder = builder. with_bucket_name ( bucket_name) ;
491
+
492
+ if let Some ( ref access_key) = config. access_key {
493
+ builder = builder. with_access_key_id ( access_key) ;
494
+ }
495
+
496
+ if let Some ( ref secret_access_key) = config. secret_access_key {
497
+ builder = builder. with_secret_access_key ( secret_access_key) ;
498
+ }
499
+
500
+ return Ok ( Box :: new ( builder. build ( ) ?) ) ;
501
+ }
502
+
503
+ return Ok ( Box :: new (
504
+ object_store:: local:: LocalFileSystem :: new_with_prefix ( data_dir. uploads_path ( ) ) ?,
505
+ ) ) ;
506
+ }
0 commit comments