Merge pull request #4 from avatao-content/webide_rework

Webide rework
This commit is contained in:
Bokros Bálint 2018-02-08 17:52:45 +01:00 committed by GitHub
commit a933248e90
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 110 additions and 56 deletions

View File

@ -51,7 +51,8 @@ ENV TFW_APP_DIR="/srv/app"
ENV TFW_FRONTEND_DIR="/srv/frontend" ENV TFW_FRONTEND_DIR="/srv/frontend"
ENV TFW_LOGIN_APP_DIR="/tmp/source_code_server" ENV TFW_LOGIN_APP_DIR="/tmp/source_code_server"
ENV TFW_TERMINADO_DIR="/tmp/terminado_server" ENV TFW_TERMINADO_DIR="/tmp/terminado_server"
ENV TFW_TERMINADO_WD="/home/${AVATAO_USER}" ENV TFW_WEBIDE_WD="/home/${AVATAO_USER}/workdir"
ENV TFW_TERMINADO_WD=${TFW_WEBIDE_WD}
ENV TFW_LIB_DIR="/usr/local/lib/" ENV TFW_LIB_DIR="/usr/local/lib/"
ENV TFW_SUPERVISORD_CONF="/etc/supervisor/supervisord.conf" ENV TFW_SUPERVISORD_CONF="/etc/supervisor/supervisord.conf"
ENV TFW_SUPERVISORD_COMPONENTS="/etc/supervisor/conf" ENV TFW_SUPERVISORD_COMPONENTS="/etc/supervisor/conf"
@ -75,6 +76,12 @@ COPY src/event_handlers ${TFW_EVENT_HANDLERS_DIR}
RUN mv /data/dist ${TFW_FRONTEND_DIR} RUN mv /data/dist ${TFW_FRONTEND_DIR}
COPY src/event_handlers/source_code_server/server.py ${TFW_LOGIN_APP_DIR}/
COPY src/event_handlers/source_code_server/users.db ${TFW_LOGIN_APP_DIR}/
COPY src/event_handlers/source_code_server/login_component.py ${TFW_WEBIDE_WD}/
RUN chown -R ${AVATAO_USER} ${TFW_WEBIDE_WD}
USER ${AVATAO_USER} USER ${AVATAO_USER}
WORKDIR /home/${AVATAO_USER} WORKDIR /home/${AVATAO_USER}

View File

@ -7,6 +7,7 @@ SUPERVISOR_HTTP_PORT = os.getenv('TFW_SUPERVISOR_PORT', 9001)
LOGIN_APP_PORT = os.getenv('TFW_LOGIN_APP_PORT', 6666) LOGIN_APP_PORT = os.getenv('TFW_LOGIN_APP_PORT', 6666)
TERMINADO_PORT = os.getenv('TFW_TERMINADO_PORT', 9999) TERMINADO_PORT = os.getenv('TFW_TERMINADO_PORT', 9999)
TERMINADO_WD = os.getenv('TFW_TERMINADO_WD') TERMINADO_WD = os.getenv('TFW_TERMINADO_WD')
WEBIDE_WD = os.getenv('TFW_WEBIDE_WD')
SUPERVISOR_HTTP_URI = 'http://localhost:{}'.format(SUPERVISOR_HTTP_PORT) SUPERVISOR_HTTP_URI = 'http://localhost:{}'.format(SUPERVISOR_HTTP_PORT)

View File

@ -1,3 +1,3 @@
import logging import logging
logging.basicConfig(level=logging.CRITICAL) logging.basicConfig(level=logging.DEBUG)

View File

@ -1,4 +1,6 @@
import xmlrpc.client, zmq import xmlrpc.client, zmq
from contextlib import suppress
from xmlrpc.client import Fault as SupervisorFault
from .config.envvars import SUPERVISOR_HTTP_URI from .config.envvars import SUPERVISOR_HTTP_URI
@ -14,6 +16,17 @@ def create_source_code_response_data(filename, content, language):
class SupervisorMixin: class SupervisorMixin:
supervisor = xmlrpc.client.ServerProxy(SUPERVISOR_HTTP_URI).supervisor supervisor = xmlrpc.client.ServerProxy(SUPERVISOR_HTTP_URI).supervisor
def stop_process(self):
with suppress(SupervisorFault):
self.supervisor.stopProcess(self.process_name)
def start_process(self):
self.supervisor.startProcess(self.process_name)
def restart_process(self):
self.stop_process()
self.start_process()
class ZMQConnectorBase: class ZMQConnectorBase:
def __init__(self, zmq_context=None): def __init__(self, zmq_context=None):

View File

@ -2,8 +2,10 @@ from source_code_event_handler import SourceCodeEventHandler
from terminado_event_handler import TerminadoEventHandler from terminado_event_handler import TerminadoEventHandler
from tornado.ioloop import IOLoop from tornado.ioloop import IOLoop
from tfw.config.envvars import WEBIDE_WD
if __name__ == '__main__': if __name__ == '__main__':
anchor_webide = SourceCodeEventHandler('anchor_webide', 'login_component.py', 'login') anchor_webide = SourceCodeEventHandler('anchor_webide', WEBIDE_WD, 'login')
anchor_terminado = TerminadoEventHandler('anchor_terminado', 'terminado') anchor_terminado = TerminadoEventHandler('anchor_terminado', 'terminado')
IOLoop.instance().start() IOLoop.instance().start()

View File

@ -1,69 +1,99 @@
from shutil import copy, rmtree, copytree from os.path import splitext, isfile, join, relpath
from os.path import splitext from glob import glob
from contextlib import suppress
from xmlrpc.client import Fault as SupervisorFault
from tfw.util import SupervisorMixin from tfw.util import SupervisorMixin
from tfw.config import LOGIN_APP_DIR
from tfw.event_handler_base import EventHandlerBase from tfw.event_handler_base import EventHandlerBase
from tfw.config.logs import logging
log = logging.getLogger(__name__)
class FileManager:
def __init__(self, working_directory, selected_file=None):
self.exclude = ['__pycache__']
self._workdir = working_directory
self.filename = selected_file or self._relpath(self.files[0])
self.language = map_file_extension_to_language(self.filename)
def select_file(self, filename):
if not filename in self.files:
raise EnvironmentError('No such file in workdir!')
self.filename = filename
self.language = map_file_extension_to_language(self.filename)
@property
def files(self):
return [self._relpath(file) for file in glob(join(self._workdir, '**/*'), recursive=True)
if isfile(file) and
not any(word in file for word in self.exclude)]
@property
def file_contents(self):
with open(self._filepath(self.filename), 'r', errors='surrogateescape') as ifile:
return ifile.read()
@file_contents.setter
def file_contents(self, value):
with open(self._filepath(self.filename), 'w', errors='surrogateescape') as ofile:
ofile.write(value)
def _filepath(self, filename):
return join(self._workdir, filename)
def _relpath(self, filename):
return relpath(self._filepath(filename), start=self._workdir)
class SourceCodeEventHandler(EventHandlerBase, SupervisorMixin): class SourceCodeEventHandler(EventHandlerBase, SupervisorMixin):
def __init__(self, anchor, filename, process_name=None): def __init__(self, anchor, directory, process_name, selected_file=None):
super().__init__(anchor) super().__init__(anchor)
self.working_directory = LOGIN_APP_DIR self.filemanager = FileManager(directory, selected_file=selected_file)
self.filename = filename self.process_name = process_name
self.language = map_file_extension_to_language(filename)
self.process_name = process_name or splitext(filename)[0]
self.commands = { self.commands = {
'read': self.read_file, 'read': self.read,
'write': self.write_file 'write': self.write,
'select': self.select
} }
self.file = self.create_initial_state() def read(self, data):
try: data['content'] = self.filemanager.file_contents
except PermissionError: data['content'] = 'You have no permission to open that file :('
data['filename'] = self.filemanager.filename
data['language'] = self.filemanager.language
data['files'] = self.filemanager.files
return data
def write(self, data):
try: self.filemanager.file_contents = data['content']
except Exception: log.exception('Error reading file!')
data['files'] = self.filemanager.files
self.restart_process()
return data
def select(self, data):
try: self.filemanager.select_file(data['filename'])
except EnvironmentError: log.exception('Failed to select file "{}"'.format(data['filename']))
return data
def handle_event(self, anchor, data_json): def handle_event(self, anchor, data_json):
data = data_json['data'] data = data_json['data']
self.commands[data['command']](data_json) data_json['data'] = self.commands[data['command']](data)
return data_json return data_json
def handle_reset(self, data_json):
self.create_initial_state()
def read_file(self, data_json):
with open(self.file, 'r') as ifile:
content = ifile.read()
data_json['data'] = {
'filename': self.filename,
'content': content,
'language': self.language
}
def write_file(self, data_json):
data = data_json['data']
with open(self.file, 'w') as ofile:
ofile.write(data['content'])
self.supervisor.stopProcess(self.process_name)
self.supervisor.startProcess(self.process_name)
def create_initial_state(self):
with suppress(SupervisorFault):
self.supervisor.stopProcess(self.process_name)
rmtree(self.working_directory, ignore_errors=True)
copytree('source_code_server/', self.working_directory)
file = copy(self.filename, self.working_directory)
self.supervisor.startProcess(self.process_name)
return file
def map_file_extension_to_language(filename): def map_file_extension_to_language(filename):
language_map = { language_map = {
# TODO: extend, maybe auto-generate???
'.py': 'python', '.py': 'python',
'.js': 'javascript' '.js': 'javascript',
'.html': 'html',
'.css': 'css',
'.java': 'java',
'.cpp': 'c_cpp',
'.hpp': 'c_cpp',
'.c': 'c_cpp',
'.h': 'c_cpp',
'.cs': 'csharp'
} }
_, extension = splitext(filename) _, extension = splitext(filename)
return language_map[extension] return language_map.get(extension, 'text')

View File

@ -1,9 +1,10 @@
import json import json, sys
from tornado.ioloop import IOLoop from tornado.ioloop import IOLoop
from tornado.web import RequestHandler, Application from tornado.web import RequestHandler, Application
from tfw.config import LOGIN_APP_PORT from tfw.config import LOGIN_APP_PORT, WEBIDE_WD
sys.path.append(WEBIDE_WD)
from login_component import authorize_login from login_component import authorize_login

View File

@ -17,7 +17,7 @@ class TerminadoEventHandler(EventHandlerBase, SupervisorMixin):
def setup_terminado_server(self): def setup_terminado_server(self):
rmtree(self.working_directory, ignore_errors=True) rmtree(self.working_directory, ignore_errors=True)
copytree('terminado_server/', self.working_directory) copytree('terminado_server/', self.working_directory)
self.supervisor.startProcess(self.process_name) self.start_process()
def handle_event(self, anchor, data_json): def handle_event(self, anchor, data_json):
log.debug('TerminadoEventHandler received event for anchor {}'.format(anchor)) log.debug('TerminadoEventHandler received event for anchor {}'.format(anchor))

@ -1 +1 @@
Subproject commit 7e4bc12616026c10829c5b50984f6053e31d3b82 Subproject commit a6ee39156b49b003a23cce27e9be25a5e6494a1f