CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
sagemathinc

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

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