import { Router } from "express" ; import { lstat , stat , readdir , readlink , Dirent , Stats } from "fs" ; import { callback } from "awaiting" ; import { DirectoryListingEntry } from "@cocalc/util/types" ; const HOME = process . env . SMC_LOCAL_HUB_HOME ?? process . env . HOME ; export async function get_listing ( path , hidden = false ) { const dir = HOME + "/" + path ; const files = [ ] ; let file ; for ( file of await callback ( readdir , dir , { withFileTypes : true } ) ) { if ( ! hidden && file . name [ 0 ] === "." ) { continue ; } let entry ; try { JSON . stringify ( file . name ) ; entry = { name : file . name } ; } catch ( err ) { entry = { name : "????" , error : "Cannot display bad binary filename. " } ; } try { let stats ; if ( file . isSymbolicLink ( ) ) { entry . issymlink = true ; } if ( entry . issymlink ) { try { entry . link_target = await callback ( readlink , dir + "/" + entry . name ) ; } catch ( err ) { } } try { stats = await callback ( stat , dir + "/" + entry . name ) ; } catch ( err ) { stats = await callback ( lstat , dir + "/" + entry . name ) ; } entry . mtime = stats . mtime . valueOf ( ) / 1000 ; if ( stats . isDirectory ( ) ) { entry . isdir = true ; const v = await callback ( readdir , dir + "/" + entry . name ) ; if ( hidden ) { entry . size = v . length ; } else { entry . size = 0 ; for ( const x of v ) { if ( x [ 0 ] != "." ) { entry . size += 1 ; } } } } else { entry . size = stats . size ; } } catch ( err ) { entry . error = `${ entry . error ? entry . error : "" }${ err }` ; } files . push ( entry ) ; } return files ; } export default function init ( ) { const base = "/.smc/directory_listing/" ; const router = Router ( ) ; router . get ( base + "*" , async ( req , res ) => { const path = decodeURIComponent ( req . path . slice ( base . length ) . trim ( ) ) ; const { hidden } = req . query ; try { const files = await get_listing ( path , ! ! hidden ) ; res . json ( { files } ) ; } catch ( err ) { res . json ( { error : `${ err }` } ) ; } } ) ; return router ; }