Update index.js
This commit is contained in:
+141
-95
@@ -1,101 +1,147 @@
|
|||||||
<?php
|
(function (OC, window, $, undefined) {
|
||||||
|
'use strict';
|
||||||
|
$(function() {
|
||||||
|
function scrollToBottom(){
|
||||||
|
var html = $('html');
|
||||||
|
html.scrollTop(html.prop('scrollHeight'));
|
||||||
|
}
|
||||||
|
var baseUrl = OC.generateUrl('/apps/occweb');
|
||||||
|
|
||||||
namespace OCA\OCCWeb\Controller;
|
// Текущий режим терминала: 'occ' (обычные occ-команды) или 'sql'
|
||||||
|
// (выполнение произвольных SQL-запросов через /db/query).
|
||||||
|
var mode = 'occ';
|
||||||
|
|
||||||
use OCP\AppFramework\Controller;
|
var OCC_PROMPT = 'occ $ ';
|
||||||
use OCP\IRequest;
|
var SQL_PROMPT = '[[;#ff5555;]sql]# ';
|
||||||
use OCP\IDBConnection;
|
|
||||||
use OCP\AppFramework\Http\JSONResponse;
|
|
||||||
use OCP\IGroupManager;
|
|
||||||
use OCP\IUserSession;
|
|
||||||
|
|
||||||
class DbController extends Controller
|
function renderSqlResponse(term, response) {
|
||||||
{
|
if (!response) {
|
||||||
private $db;
|
term.echo('[[;#ff5555;]Empty response from server]');
|
||||||
private $groupManager;
|
return;
|
||||||
private $userSession;
|
}
|
||||||
|
if (response.success === false) {
|
||||||
public function __construct(
|
term.echo('[[;#ff5555;]Error: ]' + $.terminal.escape_formatting(response.error || 'unknown error'));
|
||||||
$AppName,
|
return;
|
||||||
IRequest $request,
|
}
|
||||||
IDBConnection $db,
|
var results = response.results || [];
|
||||||
IGroupManager $groupManager,
|
if (!results.length) {
|
||||||
IUserSession $userSession
|
term.echo('[[;yellow;]No statements were executed]');
|
||||||
) {
|
return;
|
||||||
parent::__construct($AppName, $request);
|
}
|
||||||
$this->db = $db;
|
results.forEach(function (r) {
|
||||||
$this->groupManager = $groupManager;
|
term.echo('[[;#009ae3;]> ]' + $.terminal.escape_formatting(r.query || ''));
|
||||||
$this->userSession = $userSession;
|
if (r.type === 'error') {
|
||||||
|
term.echo('[[;#ff5555;] Error: ]' + $.terminal.escape_formatting(r.error || 'unknown error'));
|
||||||
|
} else if (r.type === 'select') {
|
||||||
|
term.echo('[[;gray;] ' + r.count + ' row(s)]');
|
||||||
|
if (r.count > 0) {
|
||||||
|
term.echo($.terminal.escape_formatting(JSON.stringify(r.data, null, 2)));
|
||||||
|
}
|
||||||
|
} else if (r.type === 'set') {
|
||||||
|
term.echo('[[;green;] OK (session variable set)]');
|
||||||
|
} else {
|
||||||
|
term.echo('[[;green;] OK, ' + r.affected_rows + ' row(s) affected]');
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
function enterSqlMode(term) {
|
||||||
* @NoCSRFRequired
|
mode = 'sql';
|
||||||
*/
|
term.set_prompt(SQL_PROMPT);
|
||||||
public function query()
|
term.echo('[[;yellow;]Switched to SQL mode. Admin only — statements run directly against the database.]');
|
||||||
{
|
term.echo('[[;gray;]Separate statements with ";". Shift+Enter for a new line, Enter to run. Type "occ" to go back.]');
|
||||||
// Проверка прав администратора
|
|
||||||
$user = $this->userSession->getUser();
|
|
||||||
if (!$user) {
|
|
||||||
return new JSONResponse(['error' => 'Not authenticated'], 401);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$this->groupManager->isAdmin($user->getUID())) {
|
|
||||||
return new JSONResponse(['error' => 'Admin privileges required'], 403);
|
|
||||||
}
|
|
||||||
|
|
||||||
$sql = $this->request->getParam('sql', '');
|
|
||||||
|
|
||||||
if (empty(trim($sql))) {
|
|
||||||
return new JSONResponse(['success' => false, 'error' => 'Empty query']);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Разделяем запросы по точке с запятой. Все запросы выполняются
|
|
||||||
// последовательно на одном и том же соединении с БД (в рамках
|
|
||||||
// одного HTTP-запроса), поэтому SET сохраняет своё значение
|
|
||||||
// для current_setting() в последующих запросах пачки.
|
|
||||||
$queries = array_values(array_filter(array_map('trim', explode(';', $sql)), function ($q) {
|
|
||||||
return $q !== '';
|
|
||||||
}));
|
|
||||||
|
|
||||||
$results = [];
|
|
||||||
|
|
||||||
foreach ($queries as $query) {
|
|
||||||
$isSelect = stripos($query, 'SELECT') === 0;
|
|
||||||
$isSet = stripos($query, 'SET ') === 0;
|
|
||||||
|
|
||||||
try {
|
|
||||||
$stmt = $this->db->prepare($query);
|
|
||||||
$stmt->execute();
|
|
||||||
|
|
||||||
if ($isSelect) {
|
|
||||||
$rows = $stmt->fetchAll();
|
|
||||||
$results[] = [
|
|
||||||
'query' => $query,
|
|
||||||
'type' => 'select',
|
|
||||||
'count' => count($rows),
|
|
||||||
'data' => $rows
|
|
||||||
];
|
|
||||||
} else {
|
|
||||||
$affected = $stmt->rowCount();
|
|
||||||
$results[] = [
|
|
||||||
'query' => $query,
|
|
||||||
'type' => $isSet ? 'set' : 'write',
|
|
||||||
'affected_rows' => $affected
|
|
||||||
];
|
|
||||||
}
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
$results[] = [
|
|
||||||
'query' => $query,
|
|
||||||
'type' => 'error',
|
|
||||||
'error' => $e->getMessage()
|
|
||||||
];
|
|
||||||
// Останавливаемся на первой ошибке: не продолжаем выполнять
|
|
||||||
// оставшиеся запросы пачки (например, серию DELETE),
|
|
||||||
// если один из предыдущих шагов не выполнился.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new JSONResponse(['success' => true, 'results' => $results]);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
function exitSqlMode(term) {
|
||||||
|
mode = 'occ';
|
||||||
|
term.set_prompt(OCC_PROMPT);
|
||||||
|
term.echo('[[;yellow;]Switched back to OCC mode.]');
|
||||||
|
}
|
||||||
|
|
||||||
|
$.get(baseUrl + '/cmd', function(response){
|
||||||
|
$('#app-content').terminal(function(command, term) {
|
||||||
|
if (mode === 'sql') {
|
||||||
|
var trimmed = command.trim();
|
||||||
|
if (trimmed === 'occ') {
|
||||||
|
exitSqlMode(term);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (trimmed === 'c') {
|
||||||
|
term.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (trimmed === 'exit') {
|
||||||
|
exitSqlMode(term);
|
||||||
|
term.reset();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!trimmed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
term.pause();
|
||||||
|
$.ajax({
|
||||||
|
url: baseUrl + '/db/query',
|
||||||
|
type: 'POST',
|
||||||
|
contentType: 'application/json',
|
||||||
|
data: JSON.stringify({ sql: command })
|
||||||
|
}).done(function (response) {
|
||||||
|
renderSqlResponse(term, response);
|
||||||
|
term.resume();
|
||||||
|
}).fail(function (xhr) {
|
||||||
|
term.echo('[[;#ff5555;]Request failed: ]' + $.terminal.escape_formatting(xhr.status + ' ' + xhr.statusText));
|
||||||
|
term.resume();
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (command) {
|
||||||
|
case "c":
|
||||||
|
this.clear();
|
||||||
|
break;
|
||||||
|
case "exit":
|
||||||
|
this.reset();
|
||||||
|
break;
|
||||||
|
case "sql":
|
||||||
|
enterSqlMode(term);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
var occCommand = {
|
||||||
|
command: command
|
||||||
|
};
|
||||||
|
term.pause();
|
||||||
|
$.ajax({
|
||||||
|
url: baseUrl + '/cmd',
|
||||||
|
type: 'POST',
|
||||||
|
contentType: 'application/json',
|
||||||
|
data: JSON.stringify(occCommand)
|
||||||
|
}).done(function (response) {
|
||||||
|
term.echo('\n' + response).resume();
|
||||||
|
}).fail(function (response, code) {
|
||||||
|
term.echo('\n' + response).resume();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
greetings: function (callback) {
|
||||||
|
callback('[[;green;]' + new Date().toString().slice(0, 24) + "]\n\nPress [[;#ff5e99;]Enter] for more information on [[;#009ae3;]occ] commands.\nType [[;#ff5e99;]sql] to switch to SQL query mode.\n")
|
||||||
|
},
|
||||||
|
name: 'occ',
|
||||||
|
prompt: OCC_PROMPT,
|
||||||
|
completion: response,
|
||||||
|
keydown: function (e) {
|
||||||
|
// Shift+Enter вставляет перевод строки вместо выполнения команды,
|
||||||
|
// это позволяет набирать многострочные SQL-скрипты в режиме sql.
|
||||||
|
if (e.shiftKey && e.key === 'Enter') {
|
||||||
|
this.insert('\n');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onResize: function(){
|
||||||
|
scrollToBottom()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
$('html').keypress(function(){
|
||||||
|
scrollToBottom()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
})(OC, window, jQuery);
|
||||||
|
|||||||
Reference in New Issue
Block a user