diff --git a/src/snek/app.py b/src/snek/app.py index 2033a64..f730849 100644 --- a/src/snek/app.py +++ b/src/snek/app.py @@ -41,7 +41,7 @@ from snek.system.template import ( ) from snek.view.about import AboutHTMLView, AboutMDView from snek.view.avatar import AvatarView -from snek.view.channel import ChannelAttachmentView, ChannelView +from snek.view.channel import ChannelAttachmentView,ChannelAttachmentUploadView, ChannelView from snek.view.docs import DocsHTMLView, DocsMDView from snek.view.drive import DriveApiView, DriveView from snek.view.index import IndexView @@ -211,10 +211,10 @@ class Application(BaseApplication): # app.loop = asyncio.get_running_loop() app.executor = ThreadPoolExecutor(max_workers=200) app.loop.set_default_executor(self.executor) - for sig in (signal.SIGINT, signal.SIGTERM): - app.loop.add_signal_handler( - sig, lambda: asyncio.create_task(self.services.container.shutdown()) - ) + #for sig in (signal.SIGINT, signal.SIGTERM): + #app.loop.add_signal_handler( + # sig, lambda: asyncio.create_task(self.services.container.shutdown()) + #) async def create_task(self, task): await self.tasks.put(task) @@ -286,6 +286,9 @@ class Application(BaseApplication): self.router.add_view( "/channel/{channel_uid}/attachment.bin", ChannelAttachmentView ) + self.router.add_view( + "/channel/{channel_uid}/attachment.sock", ChannelAttachmentUploadView + ) self.router.add_view( "/channel/attachment/{relative_url:.*}", ChannelAttachmentView ) diff --git a/src/snek/static/chat-input.js b/src/snek/static/chat-input.js index a6719a6..90f3979 100644 --- a/src/snek/static/chat-input.js +++ b/src/snek/static/chat-input.js @@ -180,7 +180,13 @@ class ChatInputComponent extends NjetComponent { this.uploadButton.addEventListener("uploaded", (e) => { this.dispatchEvent(new CustomEvent("uploaded", e)); }); + this.uploadButton.addEventListener("click", (e) => { + e.preventDefault(); + this.fileUploadGrid.openFileDialog() + + }) this.subscribe("file-uploading", (e) => { + this.fileUploadGrid.style.display = "block"; this.uploadButton.style.display = "none"; diff --git a/src/snek/static/file-upload-grid.js b/src/snek/static/file-upload-grid.js index e0d5838..15767fa 100644 --- a/src/snek/static/file-upload-grid.js +++ b/src/snek/static/file-upload-grid.js @@ -1,4 +1,4 @@ -import { NjetComponent, NjetDialog } from './njet.js'; +import { NjetComponent, NjetDialog } from '/njet.js'; const FUG_ICONS = { file: 'data:image/svg+xml;utf8,' @@ -9,8 +9,11 @@ class FileUploadGrid extends NjetComponent { super(); this._grid = null; this._fileInput = null; + this.channelUid = null ; + this.uploadsDone = 0; + this.uploadsStarted = 0; } - + openFileDialog() { if(this.isBusy){ const dialog = new NjetDialog({ @@ -33,11 +36,6 @@ class FileUploadGrid extends NjetComponent { return true; } - connectedCallback() { - this.render(); - this._fileInput.addEventListener('change', e => this.handleFiles(e.target.files)); - } - render() { // Root wrapper for styling this.classList.add('fug-root'); @@ -55,8 +53,6 @@ class FileUploadGrid extends NjetComponent { this.appendChild(this._grid); this.uploadsDone = 0; this.uploadsStarted = 0; - - } reset(){ @@ -75,6 +71,11 @@ class FileUploadGrid extends NjetComponent { this.uploadsStarted = files.length; [...files].forEach(file => this.createTile(file)); } + connectedCallback() { + this.channelUid = this.getAttribute('channel'); + this.render(); + this._fileInput.addEventListener('change', e => this.handleFiles(e.target.files)); + } createTile(file) { const tile = document.createElement('div'); @@ -128,9 +129,10 @@ class FileUploadGrid extends NjetComponent { startUpload(file, tile, progress) { this.publish('file-uploading', {file: file, tile: tile, progress: progress}); - - const ws = new WebSocket(`ws://${location.host}/ws`); - ws.binaryType = 'arraybuffer'; + + const protocol = location.protocol === "https:" ? "wss://" : "ws://"; + const ws = new WebSocket(`${protocol}${location.host}/channel/${this.channelUid}/attachment.sock`); + ws.binaryType = 'arraybuffer'; let sent = 0; ws.onopen = async () => { diff --git a/src/snek/system/docker.py b/src/snek/system/docker.py index 840f361..c72620b 100644 --- a/src/snek/system/docker.py +++ b/src/snek/system/docker.py @@ -19,15 +19,17 @@ class ComposeFileManager: async def shutdown(self): print("Stopping all sessions") + + tasks = [] for name in self.list_instances(): proc = self.running_instances.get(name) if not proc: continue if proc['proc'].returncode == None: print("Stopping",name) - await proc['proc'].stop() + tasks.append(asyncio.create_task(proc['proc'].stop())) print("Stopped",name,"gracefully") - + return tasks def _load(self): try: with open(self.compose_path) as f: diff --git a/src/snek/view/channel.py b/src/snek/view/channel.py index 85cace4..178800d 100644 --- a/src/snek/view/channel.py +++ b/src/snek/view/channel.py @@ -146,6 +146,56 @@ class ChannelAttachmentView(BaseView): ) +class ChannelAttachmentUploadView(BaseView): + + + async def get(self): + + channel_uid = self.request.match_info.get("channel_uid") + user_uid = self.request.session.get("uid") + + channel_member = await self.services.channel_member.get( + user_uid=user_uid, channel_uid=channel_uid, deleted_at=None, is_banned=False + ) + + if not channel_member: + return web.HTTPNotFound() + + ws = web.WebSocketResponse() + await ws.prepare(self.request) + + file = None + filename = None + + + msg = await ws.receive() + if not msg.type == web.WSMsgType.TEXT: + return web.HTTPBadRequest() + + data = msg.json() + if not data.get('type') == 'start': + return web.HTTPBadRequest() + + filename = data['filename'] + attachment = await self.services.channel_attachment.create_file( + channel_uid=channel_uid, name=filename, user_uid=user_uid + ) + pathlib.Path(attachment["path"]).parent.mkdir(parents=True, exist_ok=True) + async with aiofiles.open(attachment["path"], "wb") as f: + async for msg in ws: + if msg.type == web.WSMsgType.BINARY: + if file is not None: + await file.write(msg.data) + await ws.send_json({"type": "progress", "filename": filename, "bytes": file.tell()}) + elif msg.type == web.WSMsgType.TEXT: + data = msg.json() + if data.get('type') == 'end': + await ws.send_json({"type": "done", "filename": filename}) + elif msg.type == web.WSMsgType.ERROR: + break + return ws + + class ChannelView(BaseView): async def get(self): channel_name = self.request.match_info.get("channel") diff --git a/src/snek/view/rpc.py b/src/snek/view/rpc.py index 0b37799..3532a3c 100644 --- a/src/snek/view/rpc.py +++ b/src/snek/view/rpc.py @@ -509,8 +509,7 @@ class RPCView(BaseView): async for msg in ws: if msg.type == web.WSMsgType.TEXT: try: - async with Profiler(): - await rpc(msg.json()) + await rpc(msg.json()) except Exception as ex: print("Deleting socket", ex, flush=True) logger.exception(ex)