├── .eslintrc.cjs ├── .gitignore ├── .prettierrc ├── CODE_OF_CONDUCT.md ├── Dockerfile ├── LICENSE ├── README.md ├── dist ├── README.md ├── bin │ └── filemanager.js └── package.json ├── jsconfig.json ├── package.json ├── postcss.config.cjs ├── src ├── app.css ├── app.html ├── global.d.ts ├── lib │ ├── components │ │ ├── Actions.svelte │ │ ├── Breadcrumb.svelte │ │ ├── Clipboard.svelte │ │ ├── CreateModal.svelte │ │ ├── FileList.svelte │ │ └── Nav.svelte │ ├── fsutils.js │ └── stores.js └── routes │ ├── [...sub] │ ├── cf-editor.svelte │ └── index.svelte │ ├── __layout.svelte │ ├── api │ ├── cf-rename.js │ ├── env │ │ └── index.js │ └── fs │ │ ├── [...sub] │ │ ├── cf-download.js │ │ ├── cf-file.js │ │ └── index.js │ │ └── cf-paste.js │ └── index.svelte ├── static └── favicon.png ├── svelte.config.js └── tailwind.config.cjs /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: ['eslint:recommended', 'prettier'], 4 | plugins: ['svelte3'], 5 | overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }], 6 | parserOptions: { 7 | sourceType: 'module', 8 | ecmaVersion: 2019 9 | }, 10 | env: { 11 | browser: true, 12 | es2017: true, 13 | node: true 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | build/ 4 | /.svelte-kit 5 | /package 6 | .env 7 | *-lock.json -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": true, 3 | "singleQuote": true, 4 | "trailingComma": "none", 5 | "printWidth": 100 6 | } 7 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | shubhendumadhukar@gmail.com. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:alpine as builder 2 | WORKDIR /app 3 | COPY package.json . 4 | RUN npm install 5 | COPY . . 6 | RUN npm run build 7 | 8 | FROM node:alpine 9 | WORKDIR /app 10 | COPY --from=builder /app/build . 11 | COPY --from=builder /app/package.json . 12 | RUN npm install --only=production 13 | EXPOSE 3000 14 | CMD ["node", "index.js"] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 The Testing Gospels 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Camouflage Filemanager 2 | 3 | Camouflage Filemanager is a web based filemanager that allows you to browse and create files/folders, edit text based files, download files. 4 | 5 | ## Preview 6 | ![CamouflageUI-1](https://raw.githubusercontent.com/testinggospels/camouflage/develop/docs/CamouflageUI-1.png) 7 | ![CamouflageUI-2](https://raw.githubusercontent.com/testinggospels/camouflage/develop/docs/CamouflageUI-2.png) 8 | ![CamouflageUI-3](https://raw.githubusercontent.com/testinggospels/camouflage/develop/docs/CamouflageUI-3.png) 9 | ![CamouflageUI-4](https://raw.githubusercontent.com/testinggospels/camouflage/develop/docs/CamouflageUI-4.png) 10 | ![CamouflageUI-5](https://raw.githubusercontent.com/testinggospels/camouflage/develop/docs/CamouflageUI-5.png) 11 | ![CamouflageUI-6](https://raw.githubusercontent.com/testinggospels/camouflage/develop/docs/CamouflageUI-6.png) 12 | 13 | ## Installation 14 | 15 | ### NPM 16 | 17 | ```bash 18 | npm install -g camouflage-filemanager 19 | ``` 20 | Filemanager requires two environment variables 21 | * `FS_ROOT`: Root Directory for filemanager; default value is current working directory 22 | * `WRITE_PROTECTED`: Default value is `true`, which means creation/deletion/updates of any kind are not allowed. 23 | 24 | Start filemanager after npm installation by running command: `filemanager` in your desired directory. Optionally you can override aforementioned environment variables by exporting or by creating a `.env` file in your current working directory. 25 | 26 | ### Docker 27 | 28 | 1. Default: 29 | ```bash 30 | docker run -d -p 3000:3000 --name filemanager shubhendumadhukar/camouflage-filemanager 31 | ``` 32 | 2. Specify root directory: 33 | ```bash 34 | docker run -d -p 3000:3000 -e FS_ROOT="/opt" --name filemanager shubhendumadhukar/camouflage-filemanager 35 | ``` 36 | 3. Specify if write is allowed (not allowed by default): 37 | ```bash 38 | docker run -d -p 3000:3000 -e FS_ROOT="/opt" -e WRITE_PROTECTED=false --name filemanager shubhendumadhukar/camouflage-filemanager 39 | ``` 40 | 4. Mount a local volume: 41 | ```bash 42 | docker run -d -p 3000:3000 -e FS_ROOT="/opt/virtual_services" -e WRITE_PROTECTED=false -v ~/Desktop:/opt/Desktop --name filemanager shubhendumadhukar/camouflage-filemanager 43 | ``` 44 | 45 | ## License 46 | ``` 47 | MIT License 48 | 49 | Copyright (c) 2021 testinggospels 50 | 51 | Permission is hereby granted, free of charge, to any person obtaining a copy 52 | of this software and associated documentation files (the "Software"), to deal 53 | in the Software without restriction, including without limitation the rights 54 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 55 | copies of the Software, and to permit persons to whom the Software is 56 | furnished to do so, subject to the following conditions: 57 | 58 | The above copyright notice and this permission notice shall be included in all 59 | copies or substantial portions of the Software. 60 | 61 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 62 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 63 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 64 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 65 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 66 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 67 | SOFTWARE. 68 | ``` 69 | -------------------------------------------------------------------------------- /dist/README.md: -------------------------------------------------------------------------------- 1 | # Camouflage Filemanager 2 | 3 | Camouflage Filemanager is a web based filemanager that allows you to browse and create files/folders, edit text based files, download files. 4 | 5 | ## Preview 6 | ![CamouflageUI-1](https://raw.githubusercontent.com/testinggospels/camouflage/develop/docs/CamouflageUI-1.png) 7 | ![CamouflageUI-2](https://raw.githubusercontent.com/testinggospels/camouflage/develop/docs/CamouflageUI-2.png) 8 | ![CamouflageUI-3](https://raw.githubusercontent.com/testinggospels/camouflage/develop/docs/CamouflageUI-3.png) 9 | 10 | ## Installation 11 | 12 | ### NPM 13 | 14 | ```bash 15 | npm install -g camouflage-filemanager 16 | ``` 17 | Filemanager requires two environment variables 18 | * `FS_ROOT`: Root Directory for filemanager; default value is current working directory 19 | * `WRITE_PROTECTED`: Default value is `true`, which means creation/deletion/updates of any kind are not allowed. 20 | 21 | Start filemanager after npm installation by running command: `filemanager` in your desired directory. Optionally you can override aforementioned environment variables by exporting or by creating a `.env` file in your current working directory. 22 | 23 | ### Docker 24 | 25 | 1. Default: 26 | ```bash 27 | docker run -d -p 3000:3000 --name filemanager shubhendumadhukar/camouflage-filemanager 28 | ``` 29 | 2. Specify root directory: 30 | ```bash 31 | docker run -d -p 3000:3000 -e FS_ROOT="/opt" --name filemanager shubhendumadhukar/camouflage-filemanager 32 | ``` 33 | 3. Specify if write is allowed (not allowed by default): 34 | ```bash 35 | docker run -d -p 3000:3000 -e FS_ROOT="/opt" -e WRITE_PROTECTED=false --name filemanager shubhendumadhukar/camouflage-filemanager 36 | ``` 37 | 4. Mount a local volume: 38 | ```bash 39 | docker run -d -p 3000:3000 -e FS_ROOT="/opt/virtual_services" -e WRITE_PROTECTED=false -v ~/Desktop:/opt/Desktop --name filemanager shubhendumadhukar/camouflage-filemanager 40 | ``` 41 | 42 | ## License 43 | ``` 44 | MIT License 45 | 46 | Copyright (c) 2021 testinggospels 47 | 48 | Permission is hereby granted, free of charge, to any person obtaining a copy 49 | of this software and associated documentation files (the "Software"), to deal 50 | in the Software without restriction, including without limitation the rights 51 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 52 | copies of the Software, and to permit persons to whom the Software is 53 | furnished to do so, subject to the following conditions: 54 | 55 | The above copyright notice and this permission notice shall be included in all 56 | copies or substantial portions of the Software. 57 | 58 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 59 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 60 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 61 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 62 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 63 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 64 | SOFTWARE. 65 | ``` -------------------------------------------------------------------------------- /dist/bin/filemanager.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import '../build/index.js' -------------------------------------------------------------------------------- /dist/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "camouflage-filemanager", 3 | "version": "0.1.1", 4 | "license": "MIT", 5 | "preferGlobal": true, 6 | "author": "Shubhendu Madhukar ", 7 | "description": "Browser based file manager for remotely hosted files.", 8 | "homepage": "https://testinggospels.github.io/camouflage/", 9 | "keywords": [ 10 | "filemanager", 11 | "camouflage", 12 | "explorer" 13 | ], 14 | "scripts": { 15 | "start": "node build/index.js" 16 | }, 17 | "main": "bin/filemanager.js", 18 | "bin": { 19 | "filemanager": "bin/filemanager.js" 20 | }, 21 | "repository": { 22 | "type": "git", 23 | "url": "https://github.com/testinggospels/camouflage-filemanager" 24 | }, 25 | "type": "module", 26 | "dependencies": { 27 | "codemirror": "^5.63.3", 28 | "daisyui": "^1.14.2", 29 | "dotenv": "^10.0.0", 30 | "fs-extra": "^10.0.0" 31 | } 32 | } -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "paths": { 5 | "$lib": ["src/lib"], 6 | "$lib/*": ["src/lib/*"] 7 | } 8 | }, 9 | "include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"] 10 | } 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "camouflage-filemanager", 3 | "version": "0.1.1", 4 | "scripts": { 5 | "dev": "svelte-kit dev", 6 | "build": "svelte-kit build", 7 | "preview": "svelte-kit preview", 8 | "lint": "prettier --ignore-path .gitignore --check --plugin-search-dir=. . && eslint --ignore-path .gitignore .", 9 | "format": "prettier --ignore-path .gitignore --write --plugin-search-dir=. ." 10 | }, 11 | "devDependencies": { 12 | "@sveltejs/adapter-node": "^1.0.0-next.53", 13 | "@sveltejs/kit": "next", 14 | "autoprefixer": "^10.3.6", 15 | "cssnano": "^5.0.8", 16 | "eslint": "^7.32.0", 17 | "eslint-config-prettier": "^8.3.0", 18 | "eslint-plugin-svelte3": "^3.2.1", 19 | "postcss": "^8.3.8", 20 | "postcss-load-config": "^3.1.0", 21 | "prettier": "^2.4.1", 22 | "prettier-plugin-svelte": "^2.4.0", 23 | "svelte": "^3.42.6", 24 | "svelte-preprocess": "^4.9.5", 25 | "tailwindcss": "^2.2.16" 26 | }, 27 | "type": "module", 28 | "dependencies": { 29 | "codemirror": "^5.63.3", 30 | "daisyui": "^1.14.2", 31 | "dotenv": "^10.0.0", 32 | "fs-extra": "^10.0.0" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /postcss.config.cjs: -------------------------------------------------------------------------------- 1 | const tailwindcss = require("tailwindcss"); 2 | const autoprefixer = require("autoprefixer"); 3 | const cssnano = require("cssnano"); 4 | 5 | const mode = process.env.NODE_ENV; 6 | const dev = mode === "development"; 7 | 8 | const config = { 9 | plugins: [ 10 | //Some plugins, like tailwindcss/nesting, need to run before Tailwind, 11 | tailwindcss(), 12 | //But others, like autoprefixer, need to run after, 13 | autoprefixer(), 14 | !dev && cssnano({ 15 | preset: "default", 16 | }) 17 | ], 18 | }; 19 | 20 | module.exports = config; -------------------------------------------------------------------------------- /src/app.css: -------------------------------------------------------------------------------- 1 | /* Write your global styles here, in PostCSS syntax */ 2 | @tailwind base; 3 | @tailwind components; 4 | @tailwind utilities -------------------------------------------------------------------------------- /src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | %svelte.head% 8 | 9 | 10 |
%svelte.body%
11 | 12 | 13 | -------------------------------------------------------------------------------- /src/global.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /src/lib/components/Actions.svelte: -------------------------------------------------------------------------------- 1 | 40 | 41 | 137 | -------------------------------------------------------------------------------- /src/lib/components/Breadcrumb.svelte: -------------------------------------------------------------------------------- 1 | 17 | 18 | 40 | -------------------------------------------------------------------------------- /src/lib/components/Clipboard.svelte: -------------------------------------------------------------------------------- 1 | 41 | 42 | {#if !CLIPBOARD_EMPTY} 43 | 62 | 63 | 110 | {/if} 111 | -------------------------------------------------------------------------------- /src/lib/components/CreateModal.svelte: -------------------------------------------------------------------------------- 1 | 19 | 20 | 21 | 28 | 34 | 35 | Create File/Folder 36 | 37 | 81 | -------------------------------------------------------------------------------- /src/lib/components/FileList.svelte: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | {#if browser} 19 | 20 | {/if} 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 | {#if WRITE_PROTECTED === 'false'} 29 | 30 | {/if} 31 | 32 | 33 | 34 | {#each files as { file, createdTime, modifiedTime, isDirectory }} 35 | 36 | {#if isDirectory} 37 | 55 | {:else} 56 | 75 | {/if} 76 | 77 | 78 | {#if WRITE_PROTECTED === 'false'} 79 | 82 | {/if} 83 | 84 | {/each} 85 | 86 |
NameCreated TimeModified TimeActions
38 | 39 | 45 | 51 | 52 | {file} 53 | 54 | 57 | 58 | 65 | 71 | 72 | {file} 73 | 74 | {createdTime}{modifiedTime} 80 | 81 |
87 |
88 | -------------------------------------------------------------------------------- /src/lib/components/Nav.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | 31 | -------------------------------------------------------------------------------- /src/lib/fsutils.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs-extra' 2 | import path from 'path'; 3 | import dotenv from 'dotenv' 4 | dotenv.config(); 5 | 6 | export function dirStat(inputFolder) { 7 | let fsRoot = path.resolve(process.env['FS_ROOT'] || process.cwd()) 8 | if (inputFolder) { 9 | fsRoot = path.join(path.resolve(process.env['FS_ROOT'] || process.cwd()), inputFolder) 10 | } 11 | const files = fs.readdirSync(fsRoot) 12 | const dirStat = []; 13 | files.forEach(file => { 14 | let filePath = path.join(fsRoot, file); 15 | let stats = fs.lstatSync(filePath) 16 | let fileDetails = { 17 | file, 18 | createdTime: stats.ctime, 19 | modifiedTime: stats.mtime, 20 | isDirectory: stats.isDirectory(), 21 | } 22 | dirStat.push(fileDetails) 23 | }); 24 | return dirStat; 25 | } 26 | 27 | export function readFile(inputFile) { 28 | const fsRoot = path.join(path.resolve(process.env['FS_ROOT'] || process.cwd()), inputFile) 29 | const isDirectory = fs.lstatSync(fsRoot).isDirectory() 30 | if (isDirectory) { 31 | return { 32 | status: 400, 33 | body: { 34 | error: 'Directory can not be treated as a text file.' 35 | } 36 | } 37 | } 38 | return fs.readFileSync(fsRoot).toString() 39 | } 40 | 41 | export function deleteFile(inputFile) { 42 | const fsRoot = path.resolve(process.env["FS_ROOT"] || process.cwd()) 43 | const delResource = path.join(fsRoot, inputFile) 44 | const parent = path.dirname(delResource) 45 | try { 46 | fs.removeSync(delResource) 47 | if (parent === fsRoot) { 48 | return dirStat(); 49 | } else { 50 | return dirStat(parent.replace(fsRoot, "")) 51 | } 52 | } catch (err) { 53 | return { error: true, message: err.message } 54 | } 55 | } 56 | 57 | export function createFolder(inputFile) { 58 | const fsRoot = path.resolve(process.env["FS_ROOT"] || process.cwd()) 59 | const file = path.join(fsRoot, inputFile) 60 | fs.mkdirpSync(file); 61 | return create(file, fsRoot) 62 | } 63 | 64 | export function createFile(inputFile) { 65 | const fsRoot = path.resolve(process.env["FS_ROOT"] || process.cwd()) 66 | const file = path.join(fsRoot, inputFile) 67 | fs.createFileSync(file); 68 | return create(file, fsRoot); 69 | } 70 | 71 | function create(createPath, fsRoot) { 72 | const parent = path.dirname(createPath) 73 | try { 74 | if (parent === fsRoot) { 75 | return dirStat() 76 | } else { 77 | return dirStat(parent.replace(fsRoot, "")) 78 | } 79 | } catch (err) { 80 | return { error: true, message: err.message } 81 | } 82 | } 83 | 84 | export function writeFile(inputFile, content) { 85 | const fsRoot = path.resolve(process.env["FS_ROOT"] || process.cwd()) 86 | const file = path.join(fsRoot, inputFile) 87 | try { 88 | fs.writeFileSync(file, content) 89 | return "success"; 90 | } catch (err) { 91 | return err.message; 92 | } 93 | } 94 | 95 | export function copy(source, dest) { 96 | try { 97 | const fsRoot = path.resolve(process.env["FS_ROOT"] || process.cwd()); 98 | const originalLocation = path.join(fsRoot, source); 99 | const newLocation = path.join(fsRoot, dest, path.basename(source)); 100 | if (!fs.existsSync(newLocation)) { 101 | fs.copySync(originalLocation, newLocation, { overwrite: false, errorOnExist: true }) 102 | } else { 103 | return { success: false, err: "dest already exists" } 104 | } 105 | return { success: true } 106 | } catch (err) { 107 | return { success: false, err: err.message } 108 | } 109 | } 110 | 111 | export function cut(source, dest) { 112 | try { 113 | const fsRoot = path.resolve(process.env["FS_ROOT"] || process.cwd()); 114 | const originalLocation = path.join(fsRoot, source); 115 | const newLocation = path.join(fsRoot, dest, path.basename(source)); 116 | if (!fs.existsSync(newLocation)) { 117 | fs.moveSync(originalLocation, newLocation, { overwrite: false, errorOnExist: true }) 118 | } else { 119 | return { success: false, err: "dest already exists" } 120 | } 121 | return { success: true } 122 | } catch (err) { 123 | return { success: false, err: err.message } 124 | } 125 | } 126 | 127 | export function rename(source, dest) { 128 | const fsRoot = path.resolve(process.env["FS_ROOT"] || process.cwd()); 129 | const oldPath = path.join(fsRoot, source) 130 | const newPath = path.join(fsRoot, dest) 131 | try { 132 | if (!fs.existsSync(newPath)) { 133 | fs.renameSync(oldPath, newPath) 134 | } else { 135 | return { success: false, err: "dest already exists" } 136 | } 137 | } catch (err) { 138 | return { success: false, err: err.message } 139 | } 140 | } -------------------------------------------------------------------------------- /src/lib/stores.js: -------------------------------------------------------------------------------- 1 | import { writable } from 'svelte/store'; 2 | import { browser } from '$app/env' 3 | let clipboard; 4 | if (browser) { 5 | clipboard = window.localStorage.getItem("clipboard") ? writable(JSON.parse(window.localStorage.getItem("clipboard"))) : writable([]); 6 | clipboard.subscribe((val) => window.localStorage.setItem("clipboard", JSON.stringify(val))) 7 | } 8 | export { clipboard } 9 | -------------------------------------------------------------------------------- /src/routes/[...sub]/cf-editor.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 59 | 60 | 61 |
62 |