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/next/lib/share/get-contents.ts
Views: 687
/*1* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45import pathToFiles from "./path-to-files";6import { promises as fs } from "fs";7import { join } from "path";8import { sortBy } from "lodash";9import { hasSpecialViewer } from "@cocalc/frontend/file-extensions";10import { getExtension } from "./util";1112const MB: number = 1000000;1314const LIMITS = {15listing: 3000, // directory listing is truncated after this many files16ipynb: 7 * MB,17sagews: 5 * MB,18whiteboard: 3 * MB,19slides: 3 * MB,20other: 1 * MB,21// no special viewer22generic: 2 * MB,23};2425// also used for proxied content -- see https://github.com/sagemathinc/cocalc/issues/802026export function getSizeLimit(path: string): number {27const ext = getExtension(path);28if (hasSpecialViewer(ext)) {29return LIMITS[ext] ?? LIMITS.other;30}31return LIMITS.generic;32}3334export interface FileInfo {35name: string;36error?: Error;37isdir?: boolean;38size?: number;39mtime?: number;40url?: string; // if given and click on this file, goes here. Can be used to make path canonical and is used for navigating github repos (say).41}4243export interface PathContents {44isdir?: boolean;45listing?: FileInfo[];46content?: string;47size?: number;48mtime?: number;49truncated?: string;50}5152export default async function getContents(53project_id: string,54path: string,55): Promise<PathContents> {56const fsPath = pathToFiles(project_id, path);57const obj: PathContents = {};5859// use lstat instead of stat so it works on symlinks too60const stats = await fs.lstat(fsPath);61obj.isdir = stats.isDirectory();62obj.mtime = stats.mtime.valueOf();63if (obj.isdir) {64// get listing65const { listing, truncated } = await getDirectoryListing(fsPath);66obj.listing = listing;67if (truncated) {68obj.truncated = truncated;69}70} else {71// get actual file content72if (stats.size >= getSizeLimit(fsPath)) {73obj.truncated = "File too big to be displayed; download it instead.";74} else {75obj.content = (await fs.readFile(fsPath)).toString();76}77obj.size = stats.size;78}79return obj;80}8182async function getDirectoryListing(83path: string,84): Promise<{ listing: FileInfo[]; truncated?: string }> {85const listing: FileInfo[] = [];86let truncated: string | undefined = undefined;87for (const name of await fs.readdir(path)) {88if (name.startsWith(".")) {89// We never grab hidden files. This is a public share server after all.90continue;91}92const obj: FileInfo = { name };93// use lstat instead of stat so it works on symlinks too94try {95const stats = await fs.lstat(join(path, name));96if (stats.isDirectory()) {97obj.isdir = true;98// For a directory, we define "size" to be the number of items99// in the directory.100obj.size = (await fs.readdir(join(path, name))).length;101} else {102obj.size = stats.size;103}104obj.mtime = stats.mtime.valueOf();105} catch (err) {106obj.error = err;107}108listing.push(obj);109if (listing.length >= LIMITS.listing) {110truncated = `Too many files -- only showing ${LIMITS.listing} of them.`;111break;112}113}114return { listing: sortBy(listing, ["name"]), truncated };115}116117118