Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/src/packages/backend/get-listing.ts
Views: 687
/*1* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45/*6Server directory listing through the HTTP server and Websocket API.78{files:[..., {size:?,name:?,mtime:?,isdir:?}]}910where mtime is integer SECONDS since epoch, size is in bytes, and isdir11is only there if true.1213Obviously we should probably use POST instead of GET, due to the14result being a function of time... but POST is so complicated.15Use ?random= or ?time= if you're worried about cacheing.16Browser client code only uses this through the websocket anyways.17*/1819import { reuseInFlight } from "@cocalc/util/reuse-in-flight";20import type { Dirent, Stats } from "node:fs";21import { lstat, readdir, readlink, stat } from "node:fs/promises";22import { getLogger } from "./logger";23import { DirectoryListingEntry } from "@cocalc/util/types";24import { join } from "path";2526const logger = getLogger("directory-listing");2728// SMC_LOCAL_HUB_HOME is used for developing cocalc inside cocalc...29const HOME = process.env.SMC_LOCAL_HUB_HOME ?? process.env.HOME ?? "";3031const getListing = reuseInFlight(32async(33path: string, // assumed in home directory!34hidden: boolean = false,35home = HOME,36): Promise<DirectoryListingEntry[]> => {37const dir = join(home, path);38logger.debug(dir);39const files: DirectoryListingEntry[] = [];40let file: Dirent;41for (file of await readdir(dir, { withFileTypes: true })) {42if (!hidden && file.name[0] === ".") {43continue;44}45let entry: DirectoryListingEntry;46try {47// I don't actually know if file.name can fail to be JSON-able with node.js -- is there48// even a string in Node.js that cannot be dumped to JSON? With python49// this definitely was a problem, but I can't find the examples now. Users50// sometimes create "insane" file names via bugs in C programs...51JSON.stringify(file.name);52entry = { name: file.name };53} catch (err) {54entry = { name: "????", error: "Cannot display bad binary filename. " };55}5657try {58let stats: Stats;59if (file.isSymbolicLink()) {60// Optimization: don't explicitly set issymlink if it is false61entry.issymlink = true;62}63if (entry.issymlink) {64// at least right now we only use this symlink stuff to display65// information to the user in a listing, and nothing else.66try {67entry.link_target = await readlink(dir + "/" + entry.name);68} catch (err) {69// If we don't know the link target for some reason; just ignore this.70}71}72try {73stats = await stat(dir + "/" + entry.name);74} catch (err) {75// don't have access to target of link (or it is a broken link).76stats = await lstat(dir + "/" + entry.name);77}78entry.mtime = stats.mtime.valueOf() / 1000;79if (stats.isDirectory()) {80entry.isdir = true;81const v = await readdir(dir + "/" + entry.name);82if (hidden) {83entry.size = v.length;84} else {85// only count non-hidden files86entry.size = 0;87for (const x of v) {88if (x[0] != ".") {89entry.size += 1;90}91}92}93} else {94entry.size = stats.size;95}96} catch (err) {97entry.error = `${entry.error ? entry.error : ""}${err}`;98}99files.push(entry);100}101return files;102},103);104105export default getListing;106107108