@@ -574,69 +574,109 @@ pub async fn get_maven_detail(storage: &Storage, path: &str) -> MavenDetail {
574574pub async fn get_npm_packages ( storage : & Storage ) -> Vec < RepoInfo > {
575575 let keys = storage. list ( "npm/" ) . await ;
576576
577- let mut packages: HashMap < String , ( RepoInfo , u64 ) > = HashMap :: new ( ) ;
577+ let mut packages: HashMap < String , RepoInfo > = HashMap :: new ( ) ;
578578
579+ // Find all metadata.json files
579580 for key in & keys {
580- if let Some ( rest) = key. strip_prefix ( "npm/" ) {
581- let parts: Vec < _ > = rest. split ( '/' ) . collect ( ) ;
582- if !parts. is_empty ( ) {
583- let name = parts[ 0 ] . to_string ( ) ;
584- let entry = packages. entry ( name. clone ( ) ) . or_insert_with ( || {
585- (
586- RepoInfo {
587- name,
588- versions : 0 ,
589- size : 0 ,
590- updated : "N/A" . to_string ( ) ,
591- } ,
592- 0 ,
593- )
594- } ) ;
595-
596- if parts. len ( ) >= 3 && parts[ 1 ] == "tarballs" {
597- entry. 0 . versions += 1 ;
598- if let Some ( meta) = storage. stat ( key) . await {
599- entry. 0 . size += meta. size ;
600- if meta. modified > entry. 1 {
601- entry. 1 = meta. modified ;
602- entry. 0 . updated = format_timestamp ( meta. modified ) ;
603- }
581+ if key. ends_with ( "/metadata.json" ) {
582+ if let Some ( name) = key
583+ . strip_prefix ( "npm/" )
584+ . and_then ( |s| s. strip_suffix ( "/metadata.json" ) )
585+ {
586+ // Parse metadata to get version count and info
587+ if let Ok ( data) = storage. get ( key) . await {
588+ if let Ok ( metadata) = serde_json:: from_slice :: < serde_json:: Value > ( & data) {
589+ let versions_count = metadata
590+ . get ( "versions" )
591+ . and_then ( |v| v. as_object ( ) )
592+ . map ( |v| v. len ( ) )
593+ . unwrap_or ( 0 ) ;
594+
595+ // Calculate total size from dist.unpackedSize or estimate
596+ let total_size: u64 = metadata
597+ . get ( "versions" )
598+ . and_then ( |v| v. as_object ( ) )
599+ . map ( |versions| {
600+ versions
601+ . values ( )
602+ . filter_map ( |v| {
603+ v. get ( "dist" )
604+ . and_then ( |d| d. get ( "unpackedSize" ) )
605+ . and_then ( |s| s. as_u64 ( ) )
606+ } )
607+ . sum ( )
608+ } )
609+ . unwrap_or ( 0 ) ;
610+
611+ // Get latest version time for "updated"
612+ let updated = metadata
613+ . get ( "time" )
614+ . and_then ( |t| t. get ( "modified" ) )
615+ . and_then ( |m| m. as_str ( ) )
616+ . map ( |s| s[ ..10 ] . to_string ( ) ) // Take just date part
617+ . unwrap_or_else ( || "N/A" . to_string ( ) ) ;
618+
619+ packages. insert (
620+ name. to_string ( ) ,
621+ RepoInfo {
622+ name : name. to_string ( ) ,
623+ versions : versions_count,
624+ size : total_size,
625+ updated,
626+ } ,
627+ ) ;
604628 }
605629 }
606630 }
607631 }
608632 }
609633
610- let mut result: Vec < _ > = packages. into_values ( ) . map ( | ( r , _ ) | r ) . collect ( ) ;
634+ let mut result: Vec < _ > = packages. into_values ( ) . collect ( ) ;
611635 result. sort_by ( |a, b| a. name . cmp ( & b. name ) ) ;
612636 result
613637}
614638
615639pub async fn get_npm_detail ( storage : & Storage , name : & str ) -> PackageDetail {
616- let prefix = format ! ( "npm/{}/tarballs/" , name) ;
617- let keys = storage. list ( & prefix) . await ;
640+ let metadata_key = format ! ( "npm/{}/metadata.json" , name) ;
618641
619642 let mut versions = Vec :: new ( ) ;
620- for key in & keys {
621- if let Some ( tarball) = key. strip_prefix ( & prefix) {
622- if let Some ( version) = tarball
623- . strip_prefix ( & format ! ( "{}-" , name) )
624- . and_then ( |s| s. strip_suffix ( ".tgz" ) )
625- {
626- let ( size, published) = if let Some ( meta) = storage. stat ( key) . await {
627- ( meta. size , format_timestamp ( meta. modified ) )
628- } else {
629- ( 0 , "N/A" . to_string ( ) )
630- } ;
631- versions. push ( VersionInfo {
632- version : version. to_string ( ) ,
633- size,
634- published,
635- } ) ;
643+
644+ // Parse metadata.json for version info
645+ if let Ok ( data) = storage. get ( & metadata_key) . await {
646+ if let Ok ( metadata) = serde_json:: from_slice :: < serde_json:: Value > ( & data) {
647+ if let Some ( versions_obj) = metadata. get ( "versions" ) . and_then ( |v| v. as_object ( ) ) {
648+ let time_obj = metadata. get ( "time" ) . and_then ( |t| t. as_object ( ) ) ;
649+
650+ for ( version, info) in versions_obj {
651+ let size = info
652+ . get ( "dist" )
653+ . and_then ( |d| d. get ( "unpackedSize" ) )
654+ . and_then ( |s| s. as_u64 ( ) )
655+ . unwrap_or ( 0 ) ;
656+
657+ let published = time_obj
658+ . and_then ( |t| t. get ( version) )
659+ . and_then ( |p| p. as_str ( ) )
660+ . map ( |s| s[ ..10 ] . to_string ( ) )
661+ . unwrap_or_else ( || "N/A" . to_string ( ) ) ;
662+
663+ versions. push ( VersionInfo {
664+ version : version. clone ( ) ,
665+ size,
666+ published,
667+ } ) ;
668+ }
636669 }
637670 }
638671 }
639672
673+ // Sort by version (semver-like, newest first)
674+ versions. sort_by ( |a, b| {
675+ let a_parts: Vec < u32 > = a. version . split ( '.' ) . filter_map ( |s| s. parse ( ) . ok ( ) ) . collect ( ) ;
676+ let b_parts: Vec < u32 > = b. version . split ( '.' ) . filter_map ( |s| s. parse ( ) . ok ( ) ) . collect ( ) ;
677+ b_parts. cmp ( & a_parts)
678+ } ) ;
679+
640680 PackageDetail { versions }
641681}
642682
0 commit comments