Commit 815586da authored by 广宏伟's avatar 广宏伟

Merged in dev (pull request #105)

Dev
parents 5fb96990 d80fe0ad
#!/usr/bin/python
#
from coco.httpd import app, socket_io
from coco.logger import create_logger
create_logger()
if __name__ == '__main__':
socket_io.run(app, debug=False)
...@@ -19,8 +19,7 @@ from .sshd import SSHServer ...@@ -19,8 +19,7 @@ from .sshd import SSHServer
from .httpd import HttpServer from .httpd import HttpServer
from .logger import create_logger from .logger import create_logger
from .tasks import TaskHandler from .tasks import TaskHandler
from .utils import get_logger, ugettext as _, \ from .utils import get_logger, ugettext as _, ignore_error
ignore_error
from .ctx import app_service from .ctx import app_service
from .recorder import get_replay_recorder from .recorder import get_replay_recorder
from .session import Session from .session import Session
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
from .app import HttpServer, app from .app import HttpServer, app, socket_io
from . import view from . import view
...@@ -6,7 +6,7 @@ from flask import Flask ...@@ -6,7 +6,7 @@ from flask import Flask
from coco.utils import get_logger from coco.utils import get_logger
from coco.config import config from coco.config import config
from coco.httpd.ws import ProxyNamespace from coco.httpd.ws import ProxyNamespace, ElfinderNamespace
logger = get_logger(__file__) logger = get_logger(__file__)
...@@ -14,9 +14,10 @@ app = Flask(__name__, template_folder='templates', static_folder='static') ...@@ -14,9 +14,10 @@ app = Flask(__name__, template_folder='templates', static_folder='static')
app.config.update(config) app.config.update(config)
socket_io = SocketIO() socket_io = SocketIO()
socket_io.on_namespace(ProxyNamespace('/ssh')) socket_io.on_namespace(ProxyNamespace('/ssh'))
socket_io.on_namespace(ElfinderNamespace('/elfinder'))
# init_kwargs = {'async_mode': 'threading'} # init_kwargs = {'async_mode': 'threading'}
init_kwargs = {'async_mode': 'eventlet'} init_kwargs = {'async_mode': 'eventlet',}
socket_io.init_app(app, **init_kwargs) socket_io.init_app(app, **init_kwargs)
socket_io.on_error_default(lambda x: logger.exception(x)) socket_io.on_error_default(lambda x: logger.exception(x))
......
import logging import logging
import json
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -33,12 +34,12 @@ class ElFinderConnector: ...@@ -33,12 +34,12 @@ class ElFinderConnector:
'cmd', 'target', 'targets[]', 'current', 'tree', 'cmd', 'target', 'targets[]', 'current', 'tree',
'name', 'content', 'src', 'dst', 'cut', 'init', 'name', 'content', 'src', 'dst', 'cut', 'init',
'type', 'width', 'height', 'upload[]', 'dirs[]', 'type', 'width', 'height', 'upload[]', 'dirs[]',
'targets' 'targets', "chunk", "range", "cid", 'reload',
] ]
_options = { _options = {
'api': _version, 'api': _version,
'uplMaxSize': '128M', 'uplMaxSize': '10M',
'options': { 'options': {
'separator': '/', 'separator': '/',
'disabled': [], 'disabled': [],
...@@ -113,13 +114,6 @@ class ElFinderConnector: ...@@ -113,13 +114,6 @@ class ElFinderConnector:
except Exception as e: except Exception as e:
self.response['error'] = '%s' % e self.response['error'] = '%s' % e
logger.exception(e) logger.exception(e)
finally:
target = self.data['target']
if not target:
return
volume = self.get_volume(target)
if volume:
volume.close()
def get_request_data(self): def get_request_data(self):
data_source = {} data_source = {}
...@@ -221,10 +215,11 @@ class ElFinderConnector: ...@@ -221,10 +215,11 @@ class ElFinderConnector:
volume = list(self.volumes.values())[0] volume = list(self.volumes.values())[0]
else: else:
volume = self.get_volume(target) volume = self.get_volume(target)
self.response['cwd'] = volume.info(target) self.response['cwd'] = volume.info(target)
files = volume.list(target) files = volume.list(target)
if 'tree' in self.data: if 'tree' in self.data or 'reload' in self.data:
parents = volume.parents(target, depth=0) parents = volume.parents(target, depth=0)
parents = filter(lambda x: x not in files, parents) parents = filter(lambda x: x not in files, parents)
files += parents files += parents
...@@ -282,7 +277,18 @@ class ElFinderConnector: ...@@ -282,7 +277,18 @@ class ElFinderConnector:
def __upload(self): def __upload(self):
parent = self.data['target'] parent = self.data['target']
volume = self.get_volume(parent) volume = self.get_volume(parent)
self.response.update(volume.upload(self.request.files, parent)) if self.data.get('chunk') and self.data.get('cid'):
self.response.update(
volume.upload_as_chunk(
self.request.files, self.data.get('chunk'), parent
)
)
elif self.data.get('chunk'):
self.response.update(
volume.upload_chunk_merge(parent, self.data.get('chunk'))
)
else:
self.response.update(volume.upload(self.request.files, parent))
def __size(self): def __size(self):
target = self.data['target'] target = self.data['target']
......
...@@ -223,3 +223,13 @@ class BaseVolume: ...@@ -223,3 +223,13 @@ class BaseVolume:
:returns: TODO :returns: TODO
""" """
raise NotImplementedError raise NotImplementedError
def upload_as_chunk(self, files, chunk_name, parent):
"""
Upload a large file as chunk
:param files:
:param chunk_name:
:param cid:
:param parent:
:return:
"""
import logging
import stat import stat
import threading
from flask import send_file from flask import send_file
from coco.utils import get_logger
from .base import BaseVolume from .base import BaseVolume
logger = get_logger(__name__)
logger = logging.getLogger(__name__)
class SFTPVolume(BaseVolume): class SFTPVolume(BaseVolume):
...@@ -15,6 +15,7 @@ class SFTPVolume(BaseVolume): ...@@ -15,6 +15,7 @@ class SFTPVolume(BaseVolume):
self.root_name = 'Home' self.root_name = 'Home'
super().__init__() super().__init__()
self._stat_cache = {} self._stat_cache = {}
self.lock = threading.Lock()
def close(self): def close(self):
self.sftp.close() self.sftp.close()
...@@ -94,7 +95,7 @@ class SFTPVolume(BaseVolume): ...@@ -94,7 +95,7 @@ class SFTPVolume(BaseVolume):
""" Get the sub directory of directory """ Get the sub directory of directory
""" """
path = self._path(target) path = self._path(target)
# print("Tree {} {}".format(target, path)) print("Tree {} {}".format(target, path))
infos = self.list(target) infos = self.list(target)
tree = list(filter(lambda x: x['mime'] == 'directory', infos)) tree = list(filter(lambda x: x['mime'] == 'directory', infos))
return tree return tree
...@@ -198,6 +199,36 @@ class SFTPVolume(BaseVolume): ...@@ -198,6 +199,36 @@ class SFTPVolume(BaseVolume):
added.append(self._info(path)) added.append(self._info(path))
return {'added': added} return {'added': added}
def upload_as_chunk(self, files, chunk_name, parent):
added = []
print("Upload chunk: {}".format(chunk_name))
parent_path = self._path(parent)
item = files.get('upload[]')
__tmp = chunk_name.split('.')
filename = '.'.join(__tmp[:-2])
num, total = __tmp[-2].split('_')
num, total = int(num), int(total)
path = self._join(parent_path, filename)
remote_path = self._remote_path(path)
if num == 0:
infos = self._list(parent_path)
files_exist = [d['name'] for d in infos]
if item.filename in files_exist:
raise OSError("File {} exits".format(remote_path))
with self.sftp.open(remote_path, 'a') as rf:
for data in item:
rf.write(data)
if num != total:
return {'added': added}
else:
return {'added': added, '_chunkmerged': filename, '_name': filename}
def upload_chunk_merge(self, parent, chunk):
parent_path = self._path(parent)
path = self._join(parent_path, chunk)
return {"added": [self._info(path)]}
def size(self, target): def size(self, target):
info = self.info(target) info = self.info(target)
return info.get('size') or 'Unknown' return info.get('size') or 'Unknown'
This diff is collapsed.
...@@ -6,8 +6,17 @@ ...@@ -6,8 +6,17 @@
<link rel="stylesheet" type="text/css" media="screen" href="{{ url_for('static', filename='elfinder/css/theme-gray.css') }}"> <link rel="stylesheet" type="text/css" media="screen" href="{{ url_for('static', filename='elfinder/css/theme-gray.css') }}">
<script type="text/javascript" src="{{ url_for('static', filename='elfinder/elfinder.full.js') }}"></script> <script type="text/javascript" src="{{ url_for('static', filename='elfinder/elfinder.full.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='elfinder/i18n/elfinder.pl.js') }}"></script> <script type="text/javascript" src="{{ url_for('static', filename='elfinder/i18n/elfinder.pl.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/socket.io.js') }}"></script>
<script type="text/javascript" charset="utf-8"> <script type="text/javascript" charset="utf-8">
$().ready(function () { var socket = io.connect('/elfinder');
socket.on('connect', function () {
console.log("Connect websocket done")
});
socket.on('data', function (msg) {
var sid = msg.sid;
init_elfinder(sid);
});
function init_elfinder(sid) {
var elf; var elf;
var opts = { var opts = {
uiOptions : { uiOptions : {
...@@ -20,8 +29,10 @@ ...@@ -20,8 +29,10 @@
['view'] ['view']
] ]
}, },
height: $(window).height(), customData: {'sid': sid},
url: '{{ url_for("sftp_connector_view") }}', height: '100%',
width: '100%',
url: '{{ url_for("sftp_host_connector_view", host=host) }}',
resizable: false, resizable: false,
lang: 'pl', lang: 'pl',
requestType: 'get', requestType: 'get',
...@@ -39,16 +50,14 @@ ...@@ -39,16 +50,14 @@
}, },
rememberLastDir: false, rememberLastDir: false,
placesFirst: false, placesFirst: false,
reloadClearHistory: true, reloadClearHistory: true
handlers: {
'open': function(event) { console.log(event.data); }
}
}; };
var start = function(lng) { var start = function(lng) {
$(function() { $(function() {
// Make elFinder (REQUIRED) // Make elFinder (REQUIRED)
opts.lang = lng; opts.lang = lng;
elf = $('#elfinder').elfinder(opts).elfinder('instance'); elf = $('#elfinder').elfinder(opts).elfinder('instance');
$(window).trigger('resize');
}); });
}; };
var load = function () { var load = function () {
...@@ -84,8 +93,6 @@ ...@@ -84,8 +93,6 @@
} else { } else {
start(lng); start(lng);
} }
}; };
load(); load();
var resizeTimer; var resizeTimer;
...@@ -93,14 +100,26 @@ ...@@ -93,14 +100,26 @@
resizeTimer && clearTimeout(resizeTimer); resizeTimer && clearTimeout(resizeTimer);
if (!$('#elfinder').hasClass('elfinder-fullscreen')) { if (!$('#elfinder').hasClass('elfinder-fullscreen')) {
resizeTimer = setTimeout(function () { resizeTimer = setTimeout(function () {
var h = parseInt($(window).height()); var h, w;
if (h != parseInt($('#elfinder').height())) { var isTrue = window == parent;
elf.resize('100%', h); console.log("Window == parent" + isTrue);
if (window != parent) {
h = parseInt(parent.$('.window.active').height());
w = parseInt(parent.$('.window.active').width());
} else {
h = parseInt($(window).height());
w = parseInt($(window).width());
}
var ori_h = parseInt($('#elfinder').height());
var ori_w = parseInt($('#elfinder').width());
console.log("Height: " + h + " Wid: " + w);
if (h !== ori_h || w != ori_w){
elf.resize(w, h);
} }
}, 200); }, 200);
} }
}); });
}); }
</script> </script>
<div id="elfinder"></div> <div id="elfinder"></div>
</body> </body>
......
<html>
<head>
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery-2.1.1.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery-ui-1.10.4.min.js') }}"></script>
<link rel="stylesheet" type="text/css" media="screen" href="{{ url_for('static', filename='elfinder/css/elfinder.min.css') }}">
<link rel="stylesheet" type="text/css" media="screen" href="{{ url_for('static', filename='elfinder/css/theme-gray.css') }}">
<script type="text/javascript" src="{{ url_for('static', filename='elfinder/elfinder.min.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='elfinder/i18n/elfinder.pl.js') }}"></script>
</head>
<body style="margin: 0">
<div id="elfinder"></div>
<script type="text/javascript" charset="utf-8">
$(document).ready(function () {
var elf;
var opts = {
uiOptions : {
toolbar: [
['back', 'forward'],
['mkdir', 'mkfile'],
['copy', 'cut', 'paste'],
['rm'],
['rename'],
['view']
]
},
// ui: [],
height: $(window).height(),
url: '{{ url_for("sftp_host_connector_view", host=host) }}',
resizable: false,
lang: 'pl',
rememberLastDir: false,
reloadClearHistory: true,
contextmenu: {
navbar: [
'rm'
],
cwd: [
'reload', 'back', '|', 'mkdir', 'mkfile', '|',
'upload'
],
files: [
'rm', 'rename', 'download'
]
},
placesFirst: false
};
var start = function(lng) {
$(function() {
// Make elFinder (REQUIRED)
opts.lang = lng;
elf = $('#elfinder').elfinder(opts).elfinder('instance');
});
};
var load = function () {
var loct = window.location.search;
var full_lng, locm, lng;
if (loct && (locm = loct.match(/lang=([a-zA-Z_-]+)/))) {
full_lng = locm[1];
} else {
full_lng = (navigator.browserLanguage || navigator.language || navigator.userLanguage);
}
lng = full_lng.substr(0,2);
if (lng === 'ja') {
lng = 'jp';
}
else if (lng === 'pt') {
lng = 'pt_BR';
}
else if (lng === 'zh') {
lng = (full_lng.substr(0,5) == 'zh-tw')? 'zh_TW' : 'zh_CN';
}
if (lng !== 'en') {
$.ajax({
url : "{{ url_for('static', filename='elfinder/i18n/') }}"+'/elfinder.'+lng+'.js',
cache : true,
dataType : 'script'
})
.done(function() {
start(lng);
})
.fail(function() {
start('en');
});
} else {
start(lng);
}
};
load();
var resizeTimer;
$(window).resize(function () {
resizeTimer && clearTimeout(resizeTimer);
if (!$('#elfinder').hasClass('elfinder-fullscreen')) {
resizeTimer = setTimeout(function () {
var h = parseInt($(window).height());
if (h != parseInt($('#elfinder').height())) {
elf.resize('100%', h);
}
}, 200);
}
});
});
</script>
</body>
</html>
# -*- coding: utf-8 -*-
#
__sftp_cached = {}
def get_cached_sftp(sid):
return __sftp_cached.get(sid)
def set_cache_sftp(sid, volume):
__sftp_cached[sid] = volume
...@@ -3,23 +3,32 @@ ...@@ -3,23 +3,32 @@
from flask import render_template, request, jsonify from flask import render_template, request, jsonify
from coco.utils import get_logger
from .app import app from .app import app
from .finder import connector, volumes from .elfinder import connector, volumes
from ..models import Connection from ..models import Connection
from ..sftp import InternalSFTPClient from ..sftp import InternalSFTPClient
from .auth import login_required from .auth import login_required
from .utils import get_cached_sftp, set_cache_sftp
from ..service import app_service from ..service import app_service
logger = get_logger(__file__)
@app.route('/elfinder/sftp/connector/<host>/', methods=['GET', 'POST'])
@app.route('/coco/elfinder/sftp/connector/<host>/', methods=['GET', 'POST'])
@login_required @login_required
def sftp_host_connector_view(host): def sftp_host_connector_view(host):
user = request.current_user sid = request.args.get("sid") or request.values.get('sid')
connection = Connection(addr=(request.real_ip, 0)) sftp = get_cached_sftp(sid) if sid else None
connection.user = user if not sftp:
sftp = InternalSFTPClient(connection) logger.debug("New sftp, sid: {} host: {}".format(sid, host))
user = request.current_user
connection = Connection(addr=(request.real_ip, 0))
connection.user = user
sftp = InternalSFTPClient(connection)
set_cache_sftp(sid, sftp)
volume = volumes.SFTPVolume(sftp) volume = volumes.SFTPVolume(sftp)
if host: if host != '_':
asset = app_service.get_asset(host) asset = app_service.get_asset(host)
if not asset: if not asset:
return jsonify({'error': 'Not found this host'}) return jsonify({'error': 'Not found this host'})
...@@ -28,31 +37,25 @@ def sftp_host_connector_view(host): ...@@ -28,31 +37,25 @@ def sftp_host_connector_view(host):
hostname = "{}.{}".format(asset.hostname, asset.org_name) hostname = "{}.{}".format(asset.hostname, asset.org_name)
volume.root_name = hostname volume.root_name = hostname
volume.base_path = '/' + hostname volume.base_path = '/' + hostname
handler = connector.ElFinderConnector([volume]) handler = connector.ElFinderConnector([volume])
handler.run(request) handler.run(request)
# Some commands (e.g. read file) will return a Django View - if it # If download file, return a view response
# is set, return it directly instead of building a response
if handler.return_view: if handler.return_view:
return handler.return_view return handler.return_view
if handler.headers['Content-type'] == 'application/json': if handler.headers['Content-type'] == 'application/json':
return jsonify(handler.response) return jsonify(handler.response)
@app.route('/elfinder/sftp/connector/', methods=['GET', 'POST']) @app.route('/coco/elfinder/sftp/<host>/')
@login_required
def sftp_connector_view():
return sftp_host_connector_view('')
@app.route('/elfinder/sftp/<host>/')
def sftp_host_finder(host): def sftp_host_finder(host):
return render_template('finder/host_file_manager.html', host=host) return render_template('elfinder/file_manager.html', host=host)
@app.route('/elfinder/sftp/') @app.route('/coco/elfinder/sftp/')
def sftp_finder(): def sftp_finder():
return render_template('finder/file_manager.html') return render_template('elfinder/file_manager.html', host='_')
...@@ -3,14 +3,15 @@ ...@@ -3,14 +3,15 @@
import os import os
import uuid import uuid
from flask_socketio import SocketIO, Namespace, join_room from flask_socketio import join_room
from flask import Flask, request from flask import request
from ..models import Connection, WSProxy from ..models import Connection, WSProxy
from ..proxy import ProxyServer from ..proxy import ProxyServer
from ..utils import get_logger from ..utils import get_logger
from ..ctx import app_service from ..ctx import app_service
from .base import BaseNamespace from .base import BaseNamespace
from .utils import get_cached_sftp
BASE_DIR = os.path.dirname(os.path.dirname(__file__)) BASE_DIR = os.path.dirname(os.path.dirname(__file__))
logger = get_logger(__file__) logger = get_logger(__file__)
...@@ -192,4 +193,14 @@ class ProxyNamespace(BaseNamespace): ...@@ -192,4 +193,14 @@ class ProxyNamespace(BaseNamespace):
pass pass
def on_ping(self): def on_ping(self):
self.emit('pong') self.emit('pong')
\ No newline at end of file
class ElfinderNamespace(BaseNamespace):
def on_connect(self):
self.emit('data', {"sid": str(request.sid)})
def on_disconnect(self):
sftp = get_cached_sftp(request.sid)
if sftp:
sftp.close()
...@@ -27,11 +27,13 @@ class SFTPServer(paramiko.SFTPServerInterface): ...@@ -27,11 +27,13 @@ class SFTPServer(paramiko.SFTPServerInterface):
trans = chan.get_transport() trans = chan.get_transport()
sftp.close() sftp.close()
if [c for c in trans._channels.values() if not c.closed]: active_channels = [c for c in trans._channels.values() if not c.closed]
if not active_channels:
print("CLose transport")
trans.close() trans.close()
if sock: if sock:
sock.close() sock.close()
sock.transport.close() sock.transport.close()
self._sftp = {} self._sftp = {}
def get_host_sftp(self, host, su): def get_host_sftp(self, host, su):
...@@ -100,7 +102,6 @@ class SFTPServer(paramiko.SFTPServerInterface): ...@@ -100,7 +102,6 @@ class SFTPServer(paramiko.SFTPServerInterface):
return False return False
def create_ftp_log(self, path, operate, is_success=True, filename=None): def create_ftp_log(self, path, operate, is_success=True, filename=None):
print("Create ftp log: {} {} {}".format(path, operate, filename))
host, su, rpath = self.parse_path(path) host, su, rpath = self.parse_path(path)
asset = self.hosts.get(host) asset = self.hosts.get(host)
date_start = datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S") + " +0000", date_start = datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S") + " +0000",
...@@ -361,6 +362,7 @@ class InternalSFTPClient(SFTPServer): ...@@ -361,6 +362,7 @@ class InternalSFTPClient(SFTPServer):
except OSError: except OSError:
raise OSError("Open file {} failed".format(rpath)) raise OSError("Open file {} failed".format(rpath))
finally: finally:
print("Open success")
self.create_ftp_log(path, operate, success) self.create_ftp_log(path, operate, success)
return f return f
...@@ -371,4 +373,5 @@ class InternalSFTPClient(SFTPServer): ...@@ -371,4 +373,5 @@ class InternalSFTPClient(SFTPServer):
return self.remove(path) return self.remove(path)
def close(self): def close(self):
print("End ")
return self.session_ended() return self.session_ended()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment