11import { Express , Response } from "express" ;
2+ import { randomBytes } from "crypto" ;
23import { Filters , IAdminForth , IAdminUserExpressRequest } from "adminforth" ;
34import * as z from "zod" ;
45import TwoFactorsAuthPlugin from "../plugins/adminforth-two-factors-auth/index.js" ;
5- import { levelDbAdapter , resourceAdapter , ramAdapter } from './utils.js' ;
6+ import { levelDbAdapter , resourceAdapter , ramAdapter , s3StorageAdapter } from './utils.js' ;
7+ let isLifecycleSetupDone = false ;
8+ if ( ! isLifecycleSetupDone ) {
9+ s3StorageAdapter . setupLifecycle ( ) ;
10+ isLifecycleSetupDone = true ;
11+ }
612
713const CURRENT_KV_ADAPTER = resourceAdapter ;
814const CURRENT_COLLECTION = 'dev-demo' ;
@@ -15,6 +21,7 @@ const DASHBOARD_CAR_SOURCES = [
1521 { resourceId : 'cars_ch' , label : 'ClickHouse' } ,
1622] as const ;
1723
24+
1825type DashboardCarRecord = {
1926 model : string ;
2027 price : string | number ;
@@ -232,4 +239,51 @@ export function initApi(app: Express, admin: IAdminForth) {
232239 }
233240 )
234241 ) ;
242+ app . post ( `${ admin . config . baseUrl } /api/testS3WriteStream/` ,
243+ admin . express . authorize (
244+ async ( _req : IAdminUserExpressRequest , res : Response ) => {
245+ const sizeMb = Math . min ( Math . max ( Number ( _req . body ?. sizeMb ) || 12 , 1 ) , 100 ) ;
246+ const abort = Boolean ( _req . body ?. abort ) ;
247+ const key = `dev-demo-write-stream-test/${ Date . now ( ) } -${ randomBytes ( 4 ) . toString ( 'hex' ) } .bin` ;
248+ console . log ( `[testS3WriteStream] start, key="${ key } ", sizeMb=${ sizeMb } , abort=${ abort } ` ) ;
249+
250+ const startedAt = Date . now ( ) ;
251+ try {
252+ const writer = await s3StorageAdapter . createWriteStream ( key , 'application/octet-stream' ) ;
253+ console . log ( '[testS3WriteStream] writer created, multipart upload started' ) ;
254+
255+ // 1MB of random data per write, so >5MB total gives more than one uploaded part
256+ for ( let i = 0 ; i < sizeMb ; i ++ ) {
257+ await writer . write ( randomBytes ( 1024 * 1024 ) ) ;
258+ console . log ( `[testS3WriteStream] written chunk ${ i + 1 } /${ sizeMb } (1 MB each)` ) ;
259+ }
260+
261+ if ( abort ) {
262+ await writer . abort ( ) ;
263+ console . log ( `[testS3WriteStream] aborted upload for key="${ key } "` ) ;
264+ res . json ( { ok : true , aborted : true , key, sizeMb, tookMs : Date . now ( ) - startedAt } ) ;
265+ return ;
266+ }
267+
268+ await writer . close ( ) ;
269+ console . log ( `[testS3WriteStream] closed, multipart upload completed for key="${ key } "` ) ;
270+
271+ const downloadUrl = await s3StorageAdapter . getDownloadUrl ( key , 3600 ) ;
272+ console . log ( `[testS3WriteStream] done in ${ Date . now ( ) - startedAt } ms, url=${ downloadUrl } ` ) ;
273+
274+ res . json ( {
275+ ok : true ,
276+ aborted : false ,
277+ key,
278+ sizeMb,
279+ tookMs : Date . now ( ) - startedAt ,
280+ downloadUrl,
281+ } ) ;
282+ } catch ( e ) {
283+ console . error ( '[testS3WriteStream] failed:' , e ) ;
284+ res . status ( 500 ) . json ( { ok : false , key, error : `${ e } ` } ) ;
285+ }
286+ }
287+ )
288+ ) ;
235289}
0 commit comments