This commit is contained in:
retoor 2025-06-11 17:13:58 +02:00
parent 26a54848f1
commit 3ea4918ca2
6 changed files with 83 additions and 21 deletions

View File

@ -41,7 +41,7 @@ from snek.system.template import (
) )
from snek.view.about import AboutHTMLView, AboutMDView from snek.view.about import AboutHTMLView, AboutMDView
from snek.view.avatar import AvatarView 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.docs import DocsHTMLView, DocsMDView
from snek.view.drive import DriveApiView, DriveView from snek.view.drive import DriveApiView, DriveView
from snek.view.index import IndexView from snek.view.index import IndexView
@ -211,10 +211,10 @@ class Application(BaseApplication):
# app.loop = asyncio.get_running_loop() # app.loop = asyncio.get_running_loop()
app.executor = ThreadPoolExecutor(max_workers=200) app.executor = ThreadPoolExecutor(max_workers=200)
app.loop.set_default_executor(self.executor) app.loop.set_default_executor(self.executor)
for sig in (signal.SIGINT, signal.SIGTERM): #for sig in (signal.SIGINT, signal.SIGTERM):
app.loop.add_signal_handler( #app.loop.add_signal_handler(
sig, lambda: asyncio.create_task(self.services.container.shutdown()) # sig, lambda: asyncio.create_task(self.services.container.shutdown())
) #)
async def create_task(self, task): async def create_task(self, task):
await self.tasks.put(task) await self.tasks.put(task)
@ -286,6 +286,9 @@ class Application(BaseApplication):
self.router.add_view( self.router.add_view(
"/channel/{channel_uid}/attachment.bin", ChannelAttachmentView "/channel/{channel_uid}/attachment.bin", ChannelAttachmentView
) )
self.router.add_view(
"/channel/{channel_uid}/attachment.sock", ChannelAttachmentUploadView
)
self.router.add_view( self.router.add_view(
"/channel/attachment/{relative_url:.*}", ChannelAttachmentView "/channel/attachment/{relative_url:.*}", ChannelAttachmentView
) )

View File

@ -180,7 +180,13 @@ class ChatInputComponent extends NjetComponent {
this.uploadButton.addEventListener("uploaded", (e) => { this.uploadButton.addEventListener("uploaded", (e) => {
this.dispatchEvent(new CustomEvent("uploaded", e)); this.dispatchEvent(new CustomEvent("uploaded", e));
}); });
this.uploadButton.addEventListener("click", (e) => {
e.preventDefault();
this.fileUploadGrid.openFileDialog()
})
this.subscribe("file-uploading", (e) => { this.subscribe("file-uploading", (e) => {
this.fileUploadGrid.style.display = "block"; this.fileUploadGrid.style.display = "block";
this.uploadButton.style.display = "none"; this.uploadButton.style.display = "none";

View File

@ -1,4 +1,4 @@
import { NjetComponent, NjetDialog } from './njet.js'; import { NjetComponent, NjetDialog } from '/njet.js';
const FUG_ICONS = { const FUG_ICONS = {
file: 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48"><rect x="8" y="8" width="32" height="40" rx="5" fill="white" stroke="%234af" stroke-width="2"/></svg>' file: 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48"><rect x="8" y="8" width="32" height="40" rx="5" fill="white" stroke="%234af" stroke-width="2"/></svg>'
@ -9,8 +9,11 @@ class FileUploadGrid extends NjetComponent {
super(); super();
this._grid = null; this._grid = null;
this._fileInput = null; this._fileInput = null;
this.channelUid = null ;
this.uploadsDone = 0;
this.uploadsStarted = 0;
} }
openFileDialog() { openFileDialog() {
if(this.isBusy){ if(this.isBusy){
const dialog = new NjetDialog({ const dialog = new NjetDialog({
@ -33,11 +36,6 @@ class FileUploadGrid extends NjetComponent {
return true; return true;
} }
connectedCallback() {
this.render();
this._fileInput.addEventListener('change', e => this.handleFiles(e.target.files));
}
render() { render() {
// Root wrapper for styling // Root wrapper for styling
this.classList.add('fug-root'); this.classList.add('fug-root');
@ -55,8 +53,6 @@ class FileUploadGrid extends NjetComponent {
this.appendChild(this._grid); this.appendChild(this._grid);
this.uploadsDone = 0; this.uploadsDone = 0;
this.uploadsStarted = 0; this.uploadsStarted = 0;
} }
reset(){ reset(){
@ -75,6 +71,11 @@ class FileUploadGrid extends NjetComponent {
this.uploadsStarted = files.length; this.uploadsStarted = files.length;
[...files].forEach(file => this.createTile(file)); [...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) { createTile(file) {
const tile = document.createElement('div'); const tile = document.createElement('div');
@ -128,9 +129,10 @@ class FileUploadGrid extends NjetComponent {
startUpload(file, tile, progress) { startUpload(file, tile, progress) {
this.publish('file-uploading', {file: file, tile: tile, progress: progress}); this.publish('file-uploading', {file: file, tile: tile, progress: progress});
const ws = new WebSocket(`ws://${location.host}/ws`); const protocol = location.protocol === "https:" ? "wss://" : "ws://";
ws.binaryType = 'arraybuffer'; const ws = new WebSocket(`${protocol}${location.host}/channel/${this.channelUid}/attachment.sock`);
ws.binaryType = 'arraybuffer';
let sent = 0; let sent = 0;
ws.onopen = async () => { ws.onopen = async () => {

View File

@ -19,15 +19,17 @@ class ComposeFileManager:
async def shutdown(self): async def shutdown(self):
print("Stopping all sessions") print("Stopping all sessions")
tasks = []
for name in self.list_instances(): for name in self.list_instances():
proc = self.running_instances.get(name) proc = self.running_instances.get(name)
if not proc: if not proc:
continue continue
if proc['proc'].returncode == None: if proc['proc'].returncode == None:
print("Stopping",name) print("Stopping",name)
await proc['proc'].stop() tasks.append(asyncio.create_task(proc['proc'].stop()))
print("Stopped",name,"gracefully") print("Stopped",name,"gracefully")
return tasks
def _load(self): def _load(self):
try: try:
with open(self.compose_path) as f: with open(self.compose_path) as f:

View File

@ -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): class ChannelView(BaseView):
async def get(self): async def get(self):
channel_name = self.request.match_info.get("channel") channel_name = self.request.match_info.get("channel")

View File

@ -509,8 +509,7 @@ class RPCView(BaseView):
async for msg in ws: async for msg in ws:
if msg.type == web.WSMsgType.TEXT: if msg.type == web.WSMsgType.TEXT:
try: try:
async with Profiler(): await rpc(msg.json())
await rpc(msg.json())
except Exception as ex: except Exception as ex:
print("Deleting socket", ex, flush=True) print("Deleting socket", ex, flush=True)
logger.exception(ex) logger.exception(ex)