mirror of
https://github.com/coderaiser/cloudcmd.git
synced 2026-01-23 02:35:49 +00:00
feature: cloudcmd: readonly (#405)
This commit is contained in:
parent
1d3567f3a3
commit
c710b29d6c
11 changed files with 108 additions and 3 deletions
2
HELP.md
2
HELP.md
|
|
@ -88,6 +88,7 @@ Cloud Commander supports the following command-line parameters:
|
||||||
| `--config-auth` | enable auth change in config dialog
|
| `--config-auth` | enable auth change in config dialog
|
||||||
| `--console` | enable console
|
| `--console` | enable console
|
||||||
| `--sync-console-path` | sync console path
|
| `--sync-console-path` | sync console path
|
||||||
|
| `--readonly` | disable ui related to filesystem modifications
|
||||||
| `--terminal` | enable terminal
|
| `--terminal` | enable terminal
|
||||||
| `--terminal-path` | set terminal path
|
| `--terminal-path` | set terminal path
|
||||||
| `--terminal-command` | set command to run in terminal (shell by default)
|
| `--terminal-command` | set command to run in terminal (shell by default)
|
||||||
|
|
@ -403,6 +404,7 @@ Here's a description of all options:
|
||||||
"configAuth": true, // enable auth change in config dialog
|
"configAuth": true, // enable auth change in config dialog
|
||||||
"console": true, // enable console
|
"console": true, // enable console
|
||||||
"syncConsolePath": false, // do not sync console path
|
"syncConsolePath": false, // do not sync console path
|
||||||
|
"readonly": false, // disable ui related to filesystem modifications
|
||||||
"terminal": false, // disable terminal
|
"terminal": false, // disable terminal
|
||||||
"terminalPath": "", // path of a terminal
|
"terminalPath": "", // path of a terminal
|
||||||
"terminalCommand": "", // set command to run in terminal
|
"terminalCommand": "", // set command to run in terminal
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,7 @@ const yargsOptions = {
|
||||||
'config-auth',
|
'config-auth',
|
||||||
'console',
|
'console',
|
||||||
'sync-console-path',
|
'sync-console-path',
|
||||||
|
'readonly',
|
||||||
'contact',
|
'contact',
|
||||||
'terminal',
|
'terminal',
|
||||||
'terminal-auto-restart',
|
'terminal-auto-restart',
|
||||||
|
|
@ -105,6 +106,7 @@ const yargsOptions = {
|
||||||
'port': config('port'),
|
'port': config('port'),
|
||||||
'online': config('online'),
|
'online': config('online'),
|
||||||
'open': choose(env.bool('open'), config('open')),
|
'open': choose(env.bool('open'), config('open')),
|
||||||
|
'readonly': env.bool('readonly') || config('readonly'),
|
||||||
'editor': env('editor') || config('editor'),
|
'editor': env('editor') || config('editor'),
|
||||||
'packer': config('packer') || 'tar',
|
'packer': config('packer') || 'tar',
|
||||||
'zip': config('zip'),
|
'zip': config('zip'),
|
||||||
|
|
@ -205,6 +207,8 @@ async function main() {
|
||||||
config('configDialog', args.configDialog);
|
config('configDialog', args.configDialog);
|
||||||
config('configAuth', args.configAuth);
|
config('configAuth', args.configAuth);
|
||||||
config('keysPanel', args.keysPanel);
|
config('keysPanel', args.keysPanel);
|
||||||
|
config('readonly', args.readonly);
|
||||||
|
|
||||||
config('export', args.export);
|
config('export', args.export);
|
||||||
config('exportToken', args.exportToken);
|
config('exportToken', args.exportToken);
|
||||||
config('import', args.import);
|
config('import', args.import);
|
||||||
|
|
|
||||||
|
|
@ -169,10 +169,10 @@ function getFileMenuData() {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const menuDataFile = {
|
const menuDataFile = maybeReadonly({
|
||||||
...menuTop,
|
...menuTop,
|
||||||
...menuBottom,
|
...menuBottom,
|
||||||
};
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isAuth,
|
isAuth,
|
||||||
|
|
@ -180,6 +180,28 @@ function getFileMenuData() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function maybeReadonly(menu) {
|
||||||
|
const operations = [
|
||||||
|
'Edit',
|
||||||
|
'Rename',
|
||||||
|
'Delete',
|
||||||
|
'Pack',
|
||||||
|
'Extract',
|
||||||
|
'Cut',
|
||||||
|
'Copy',
|
||||||
|
'Paste',
|
||||||
|
'New',
|
||||||
|
'Upload',
|
||||||
|
'Upload From Cloud',
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const operation of operations) {
|
||||||
|
delete menu[operation];
|
||||||
|
}
|
||||||
|
|
||||||
|
return menu;
|
||||||
|
}
|
||||||
|
|
||||||
function isCurrent(yesFn, noFn) {
|
function isCurrent(yesFn, noFn) {
|
||||||
if (Info.name !== '..')
|
if (Info.name !== '..')
|
||||||
return yesFn();
|
return yesFn();
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,9 @@ const noFilesCheck = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.init = promisify((callback) => {
|
module.exports.init = promisify((callback) => {
|
||||||
|
if (config('readonly'))
|
||||||
|
return;
|
||||||
|
|
||||||
showLoad();
|
showLoad();
|
||||||
|
|
||||||
exec.series([
|
exec.series([
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@
|
||||||
"root": "/",
|
"root": "/",
|
||||||
"prefix": "",
|
"prefix": "",
|
||||||
"prefixSocket": "",
|
"prefixSocket": "",
|
||||||
|
"readonly": false,
|
||||||
"contact": true,
|
"contact": true,
|
||||||
"confirmCopy": true,
|
"confirmCopy": true,
|
||||||
"confirmMove": true,
|
"confirmMove": true,
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ const html = fs.readFileSync(getIndexPath(isDev), 'utf8');
|
||||||
const initAuth = currify(_initAuth);
|
const initAuth = currify(_initAuth);
|
||||||
const notEmpty = (a) => a;
|
const notEmpty = (a) => a;
|
||||||
const clean = (a) => a.filter(notEmpty);
|
const clean = (a) => a.filter(notEmpty);
|
||||||
|
const createReadonly = (config) => () => config('readonly');
|
||||||
|
|
||||||
const isUndefined = (a) => typeof a === 'undefined';
|
const isUndefined = (a) => typeof a === 'undefined';
|
||||||
const isFn = (a) => typeof a === 'function';
|
const isFn = (a) => typeof a === 'function';
|
||||||
|
|
@ -131,6 +132,7 @@ function _initAuth(config, accept, reject, username, password) {
|
||||||
function listen({prefixSocket, socket, config}) {
|
function listen({prefixSocket, socket, config}) {
|
||||||
const root = apart(config, 'root');
|
const root = apart(config, 'root');
|
||||||
const auth = initAuth(config);
|
const auth = initAuth(config);
|
||||||
|
const readonly = createReadonly(config);
|
||||||
|
|
||||||
prefixSocket = getPrefix(prefixSocket);
|
prefixSocket = getPrefix(prefixSocket);
|
||||||
config.listen(socket, auth);
|
config.listen(socket, auth);
|
||||||
|
|
@ -138,18 +140,21 @@ function listen({prefixSocket, socket, config}) {
|
||||||
edward.listen(socket, {
|
edward.listen(socket, {
|
||||||
root,
|
root,
|
||||||
auth,
|
auth,
|
||||||
|
readonly,
|
||||||
prefixSocket: `${prefixSocket}/edward`,
|
prefixSocket: `${prefixSocket}/edward`,
|
||||||
});
|
});
|
||||||
|
|
||||||
dword.listen(socket, {
|
dword.listen(socket, {
|
||||||
root,
|
root,
|
||||||
auth,
|
auth,
|
||||||
|
readonly,
|
||||||
prefixSocket: `${prefixSocket}/dword`,
|
prefixSocket: `${prefixSocket}/dword`,
|
||||||
});
|
});
|
||||||
|
|
||||||
deepword.listen(socket, {
|
deepword.listen(socket, {
|
||||||
root,
|
root,
|
||||||
auth,
|
auth,
|
||||||
|
readonly,
|
||||||
prefixSocket: `${prefixSocket}/deepword`,
|
prefixSocket: `${prefixSocket}/deepword`,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -161,6 +166,7 @@ function listen({prefixSocket, socket, config}) {
|
||||||
fileop.listen(socket, {
|
fileop.listen(socket, {
|
||||||
root,
|
root,
|
||||||
auth,
|
auth,
|
||||||
|
readonly,
|
||||||
prefix: `${prefixSocket}/fileop`,
|
prefix: `${prefixSocket}/fileop`,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -180,6 +186,7 @@ function cloudcmd({modules, config}) {
|
||||||
const diff = apart(config, 'diff');
|
const diff = apart(config, 'diff');
|
||||||
const zip = apart(config, 'zip');
|
const zip = apart(config, 'zip');
|
||||||
const root = apart(config, 'root');
|
const root = apart(config, 'root');
|
||||||
|
const readonly = createReadonly(config);
|
||||||
|
|
||||||
const ponseStatic = ponse.static({
|
const ponseStatic = ponse.static({
|
||||||
cache,
|
cache,
|
||||||
|
|
@ -201,6 +208,7 @@ function cloudcmd({modules, config}) {
|
||||||
zip,
|
zip,
|
||||||
dropbox,
|
dropbox,
|
||||||
dropboxToken,
|
dropboxToken,
|
||||||
|
readonly,
|
||||||
}),
|
}),
|
||||||
dword({
|
dword({
|
||||||
root,
|
root,
|
||||||
|
|
@ -209,6 +217,7 @@ function cloudcmd({modules, config}) {
|
||||||
zip,
|
zip,
|
||||||
dropbox,
|
dropbox,
|
||||||
dropboxToken,
|
dropboxToken,
|
||||||
|
readonly,
|
||||||
}),
|
}),
|
||||||
deepword({
|
deepword({
|
||||||
root,
|
root,
|
||||||
|
|
@ -217,6 +226,7 @@ function cloudcmd({modules, config}) {
|
||||||
zip,
|
zip,
|
||||||
dropbox,
|
dropbox,
|
||||||
dropboxToken,
|
dropboxToken,
|
||||||
|
readonly,
|
||||||
}),
|
}),
|
||||||
fileop(),
|
fileop(),
|
||||||
nomine(),
|
nomine(),
|
||||||
|
|
@ -233,6 +243,7 @@ function cloudcmd({modules, config}) {
|
||||||
}),
|
}),
|
||||||
restafary({
|
restafary({
|
||||||
prefix: cloudfunc.apiURL + '/fs',
|
prefix: cloudfunc.apiURL + '/fs',
|
||||||
|
readonly,
|
||||||
root,
|
root,
|
||||||
}),
|
}),
|
||||||
userMenu({
|
userMenu({
|
||||||
|
|
|
||||||
|
|
@ -188,6 +188,9 @@ module.exports._onPUT = onPUT;
|
||||||
function onPUT({name, config, body}, callback) {
|
function onPUT({name, config, body}, callback) {
|
||||||
checkPut(name, body, callback);
|
checkPut(name, body, callback);
|
||||||
|
|
||||||
|
if (config('readonly'))
|
||||||
|
callback(UserError('"readonly" mode enabled'));
|
||||||
|
|
||||||
const cmd = getCMD(name);
|
const cmd = getCMD(name);
|
||||||
const files = json.parse(body);
|
const files = json.parse(body);
|
||||||
const rootDir = config('root');
|
const rootDir = config('root');
|
||||||
|
|
|
||||||
|
|
@ -111,6 +111,7 @@ async function route({config, options, request, response}) {
|
||||||
* additional processing of index file
|
* additional processing of index file
|
||||||
*/
|
*/
|
||||||
function indexProcessing(config, options) {
|
function indexProcessing(config, options) {
|
||||||
|
const readonly = config('readonly');
|
||||||
const oneFilePanel = config('oneFilePanel');
|
const oneFilePanel = config('oneFilePanel');
|
||||||
const noKeysPanel = !config('keysPanel');
|
const noKeysPanel = !config('keysPanel');
|
||||||
const noContact = !config('contact');
|
const noContact = !config('contact');
|
||||||
|
|
@ -124,10 +125,18 @@ function indexProcessing(config, options) {
|
||||||
if (noKeysPanel)
|
if (noKeysPanel)
|
||||||
data = hideKeysPanel(data);
|
data = hideKeysPanel(data);
|
||||||
|
|
||||||
if (oneFilePanel)
|
if (readonly) {
|
||||||
|
data = data
|
||||||
|
.replace('icon-move', 'icon-move none')
|
||||||
|
.replace('icon-copy', 'icon-copy none')
|
||||||
|
.replace('icon-edit', 'icon-edit none')
|
||||||
|
.replace('icon-directory', 'icon-directory none')
|
||||||
|
.replace('icon-delete', 'icon-delete none');
|
||||||
|
} else if (oneFilePanel) {
|
||||||
data = data
|
data = data
|
||||||
.replace('icon-move', 'icon-move none')
|
.replace('icon-move', 'icon-move none')
|
||||||
.replace('icon-copy', 'icon-copy none');
|
.replace('icon-copy', 'icon-copy none');
|
||||||
|
}
|
||||||
|
|
||||||
if (noContact)
|
if (noContact)
|
||||||
data = data.replace('icon-contact', 'icon-contact none');
|
data = data.replace('icon-contact', 'icon-contact none');
|
||||||
|
|
|
||||||
|
|
@ -166,6 +166,23 @@ test('cloudcmd: route: keys panel', async (t) => {
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: route: readonly', async (t) => {
|
||||||
|
const config = {
|
||||||
|
readonly: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
config,
|
||||||
|
};
|
||||||
|
|
||||||
|
const {body} = await request.get('/', {
|
||||||
|
options,
|
||||||
|
});
|
||||||
|
|
||||||
|
t.match(body, 'icon-edit none', 'should hide edit');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
test('cloudcmd: route: symlink', async (t) => {
|
test('cloudcmd: route: symlink', async (t) => {
|
||||||
const emptyDir = path.join(fixtureDir, 'empty-dir');
|
const emptyDir = path.join(fixtureDir, 'empty-dir');
|
||||||
const root = fixtureDir;
|
const root = fixtureDir;
|
||||||
|
|
|
||||||
|
|
@ -113,3 +113,30 @@ test('cloudcmd: rest: move: no to', async (t) => {
|
||||||
t.equal(body, expected);
|
t.equal(body, expected);
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('cloudcmd: rest: readonly', async (t) => {
|
||||||
|
const cloudcmd = reRequire(cloudcmdPath);
|
||||||
|
const {createConfigManager} = cloudcmd;
|
||||||
|
|
||||||
|
const configManager = createConfigManager();
|
||||||
|
configManager('auth', false);
|
||||||
|
configManager('root', '/');
|
||||||
|
configManager('readonly', true);
|
||||||
|
|
||||||
|
const {request} = serveOnce(cloudcmd, {
|
||||||
|
configManager,
|
||||||
|
});
|
||||||
|
|
||||||
|
const files = {
|
||||||
|
from: '/',
|
||||||
|
};
|
||||||
|
|
||||||
|
const {body} = await request.put(`/api/v1/move`, {
|
||||||
|
body: files,
|
||||||
|
});
|
||||||
|
|
||||||
|
const expected = '"readonly" mode enabled';
|
||||||
|
|
||||||
|
t.equal(body, expected);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -147,6 +147,12 @@
|
||||||
Confirm Move
|
Confirm Move
|
||||||
</label>
|
</label>
|
||||||
</li>
|
</li>
|
||||||
|
<li title="Readonly">
|
||||||
|
<label>
|
||||||
|
<input data-name="js-readonly" type="checkbox" {{ readonly }}>
|
||||||
|
Readonly
|
||||||
|
</label>
|
||||||
|
</li>
|
||||||
<li title="Synchronize path of current directory with Console">
|
<li title="Synchronize path of current directory with Console">
|
||||||
<label>
|
<label>
|
||||||
<input data-name="js-syncConsolePath" type="checkbox" {{ syncConsolePath }}>
|
<input data-name="js-syncConsolePath" type="checkbox" {{ syncConsolePath }}>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue