Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<name>OCC Web</name>
<summary>OCC Commands in a web terminal</summary>
<description><![CDATA[Run OCC Commands in a web terminal]]></description>
<version>0.2.3</version>
<version>0.3.0</version>
<licence>agpl</licence>
<author mail="adphi.apps@gmail.com" >Adphi</author>
<namespace>OCCWeb</namespace>
Expand All @@ -15,7 +15,7 @@
<repository>https://github.com/adphi/occweb</repository>
<screenshot>https://github.com/adphi/occweb/raw/main/appinfo/screenshot.png</screenshot>
<dependencies>
<nextcloud min-version="31" max-version="31"/>
<nextcloud min-version="31" max-version="33"/>
</dependencies>
<navigations>
<navigation role="admin">
Expand Down
12 changes: 12 additions & 0 deletions css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -773,3 +773,15 @@ terminal .terminal-output > div {
.prompt span, .cursor-line span span, .command div span{
color: #009ae3 !important;
}

/* Keep terminal content left-aligned even when Nextcloud runs in RTL mode */
html[dir="rtl"] #app-content.terminal,
html[dir="rtl"] #app-content .terminal,
html[dir="rtl"] #app-content .terminal-output,
html[dir="rtl"] #app-content .terminal-output > div > div,
html[dir="rtl"] #app-content .cmd,
html[dir="rtl"] #app-content .cmd div,
html[dir="rtl"] #app-content .cmd .prompt {
direction: ltr;
text-align: left;
}
40 changes: 40 additions & 0 deletions js/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,42 @@
(function (OC, window, $, undefined) {
'use strict';
$(function() {
var longRunningCommandPatterns = [
/^files:scan(?:\s|$)/,
/^files:transfer-ownership(?:\s|$)/,
/^encryption:/,
/^fulltextsearch:/,
/^preview:generate-all(?:\s|$)/,
/^versions:cleanup(?:\s|$)/,
/^trashbin:cleanup(?:\s|$)/,
/^db:add-missing-indices(?:\s|$)/,
/^db:add-missing-columns(?:\s|$)/,
/^db:add-missing-primary-keys(?:\s|$)/,
/^maintenance:repair(?:\s|$)/
];

function isPotentiallyLongRunning(command) {
var normalizedCommand = (command || '').trim().toLowerCase();
if (!normalizedCommand) {
return false;
}
return longRunningCommandPatterns.some(function(pattern) {
return pattern.test(normalizedCommand);
});
}

function confirmLongRunningCommand(command) {
if (!isPotentiallyLongRunning(command)) {
return true;
}

return window.confirm(
'Warning: "' + command + '" can take a long time on large instances and may timeout in the browser.\n\n' +
'Recommendation: Run this command via CLI/SSH for better reliability.\n\n' +
'Do you want to run it anyway?'
);
}

function scrollToBottom(){
var html = $('html');
html.scrollTop(html.prop('scrollHeight'));
Expand All @@ -16,6 +52,10 @@
this.reset();
break;
default:
if (!confirmLongRunningCommand(command)) {
term.echo('\nCommand canceled.');
break;
}
var occCommand = {
command: command
};
Expand Down
53 changes: 39 additions & 14 deletions lib/Controller/OccController.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ class OccController extends Controller
private $userId;

private $application;
private $symphonyApplication;
private $output;

public function __construct($AppName, IRequest $request, $userId)
Expand All @@ -41,14 +40,12 @@ public function __construct($AppName, IRequest $request, $userId)
);
$this->application->setAutoExit(false);
$this->output = new OccOutput(OutputInterface::VERBOSITY_NORMAL, true);
$this->application->loadCommands(new StringInput(""), $this->output);
$reflectionProperty = new \ReflectionProperty(Application::class, 'application');
$reflectionProperty->setAccessible(true);
$this->symphonyApplication = $reflectionProperty->getValue($this->application);
$this->application->loadCommands(new StringInput(""), $this->output);
}

/**
* @NoCSRFRequired
* @AdminRequired
*/
public function index()
{
Expand All @@ -73,23 +70,51 @@ private function run($input)
/**
* @param string $command
* @return DataResponse
* @AdminRequired
*/
public function cmd($command)
{
$this->logger->debug($command);
$input = new StringInput($command);
$startedAt = microtime(true);
$rawCommand = trim((string)$command);
$commandName = strtok($rawCommand, ' ') ?: 'unknown';

$this->logger->info('occweb command started', [
'user' => $this->userId,
'command' => $commandName,
]);

$input = new StringInput($rawCommand);
$response = $this->run($input);
$this->logger->debug($response);

$this->logger->info('occweb command finished', [
'user' => $this->userId,
'command' => $commandName,
'duration_ms' => (int)round((microtime(true) - $startedAt) * 1000),
]);

return new DataResponse($response);
}

/**
* @AdminRequired
*/
public function list() {
$defs = $this->symphonyApplication->all();
$cmds = array();
foreach ($defs as $d) {
array_push($cmds, $d->getName());
$output = $this->run(new StringInput('list --raw'));
$lines = preg_split('/\r\n|\r|\n/', (string)$output);
$cmds = [];

foreach ($lines as $line) {
$line = trim($line);
if ($line === '') {
continue;
}

$parts = preg_split('/\s+/', $line, 2);
if (!empty($parts[0])) {
$cmds[] = $parts[0];
}
}
return new DataResponse($cmds);

return new DataResponse(array_values(array_unique($cmds)));
}
}

10 changes: 7 additions & 3 deletions lib/Controller/OccOutput.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
class OccOutput extends BufferedOutput implements ConsoleOutputInterface
{
private $consoleSectionOutputs = [];
private $errorOutput;

private $stream;
/**
Expand All @@ -21,13 +22,16 @@ class OccOutput extends BufferedOutput implements ConsoleOutputInterface
*/
public function getErrorOutput(): OutputInterface
{
// TODO: Implement getErrorOutput() method.
return $this;
if ($this->errorOutput === null) {
// Keep compatibility when no explicit stderr output is configured.
$this->errorOutput = $this;
}
return $this->errorOutput;
}

public function setErrorOutput(OutputInterface $error)
{

$this->errorOutput = $error;
}

/**
Expand Down