Update.
This commit is contained in:
parent
3412aa0bf0
commit
9133b7c3ce
src/snek
@ -53,6 +53,7 @@ from snek.view.terminal import TerminalSocketView, TerminalView
|
||||
from snek.view.upload import UploadView
|
||||
from snek.view.user import UserView
|
||||
from snek.view.web import WebView
|
||||
from snek.view.channel import ChannelAttachmentView
|
||||
from snek.webdav import WebdavApplication
|
||||
from snek.sgit import GitApplication
|
||||
|
||||
@ -175,6 +176,8 @@ class Application(BaseApplication):
|
||||
self.router.add_get("/http-get", self.handle_http_get)
|
||||
self.router.add_get("/http-photo", self.handle_http_photo)
|
||||
self.router.add_get("/rpc.ws", RPCView)
|
||||
self.router.add_view("/channel/{channel_uid}/attachment.bin",ChannelAttachmentView)
|
||||
self.router.add_view("/channel/attachment/{relative_url:.*}",ChannelAttachmentView)
|
||||
self.router.add_view("/channel/{channel}.html", WebView)
|
||||
self.router.add_view("/threads.html", ThreadsView)
|
||||
self.router.add_view("/terminal.ws", TerminalSocketView)
|
||||
|
@ -9,6 +9,7 @@ from snek.mapper.notification import NotificationMapper
|
||||
from snek.mapper.user import UserMapper
|
||||
from snek.mapper.user_property import UserPropertyMapper
|
||||
from snek.mapper.repository import RepositoryMapper
|
||||
from snek.mapper.channel_attachment import ChannelAttachmentMapper
|
||||
from snek.system.object import Object
|
||||
|
||||
|
||||
@ -25,6 +26,7 @@ def get_mappers(app=None):
|
||||
"drive": DriveMapper(app=app),
|
||||
"user_property": UserPropertyMapper(app=app),
|
||||
"repository": RepositoryMapper(app=app),
|
||||
"channel_attachment": ChannelAttachmentMapper(app=app),
|
||||
}
|
||||
)
|
||||
|
||||
|
7
src/snek/mapper/channel_attachment.py
Normal file
7
src/snek/mapper/channel_attachment.py
Normal file
@ -0,0 +1,7 @@
|
||||
from snek.model.channel_attachment import ChannelAttachmentModel
|
||||
from snek.system.mapper import BaseMapper
|
||||
|
||||
|
||||
class ChannelAttachmentMapper(BaseMapper):
|
||||
table_name = "channel_attachment"
|
||||
model_class = ChannelAttachmentModel
|
@ -11,6 +11,7 @@ from snek.model.notification import NotificationModel
|
||||
from snek.model.user import UserModel
|
||||
from snek.model.user_property import UserPropertyModel
|
||||
from snek.model.repository import RepositoryModel
|
||||
from snek.model.channel_attachment import ChannelAttachmentModel
|
||||
from snek.system.object import Object
|
||||
|
||||
|
||||
@ -27,6 +28,7 @@ def get_models():
|
||||
"notification": NotificationModel,
|
||||
"user_property": UserPropertyModel,
|
||||
"repository": RepositoryModel,
|
||||
"channel_attachment": ChannelAttachmentModel,
|
||||
}
|
||||
)
|
||||
|
||||
|
16
src/snek/model/channel_attachment.py
Normal file
16
src/snek/model/channel_attachment.py
Normal file
@ -0,0 +1,16 @@
|
||||
from snek.system.model import BaseModel
|
||||
from snek.system.model import BaseModel, ModelField
|
||||
|
||||
|
||||
class ChannelAttachmentModel(BaseModel):
|
||||
|
||||
name = ModelField(name="name", required=True, kind=str)
|
||||
channel_uid = ModelField(name="channel_uid", required=True, kind=str)
|
||||
path = ModelField(name="path", required=True, kind=str)
|
||||
size = ModelField(name="size", required=False, kind=int)
|
||||
user_uid = ModelField(name="user_uid", required=True, kind=str)
|
||||
mime_type = ModelField(name="type", required=True, kind=str)
|
||||
relative_url = ModelField(name="relative_url", required=True, kind=str)
|
||||
resource_type = ModelField(name="resource_type", required=True, kind=str,value="file")
|
||||
|
||||
|
@ -12,6 +12,7 @@ from snek.service.user import UserService
|
||||
from snek.service.user_property import UserPropertyService
|
||||
from snek.service.util import UtilService
|
||||
from snek.service.repository import RepositoryService
|
||||
from snek.service.channel_attachment import ChannelAttachmentService
|
||||
from snek.system.object import Object
|
||||
from snek.service.db import DBService
|
||||
|
||||
@ -32,6 +33,7 @@ def get_services(app):
|
||||
"user_property": UserPropertyService(app=app),
|
||||
"repository": RepositoryService(app=app),
|
||||
"db": DBService(app=app),
|
||||
"channel_attachment": ChannelAttachmentService(app=app),
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -3,10 +3,19 @@ from datetime import datetime
|
||||
from snek.system.model import now
|
||||
from snek.system.service import BaseService
|
||||
|
||||
import pathlib
|
||||
|
||||
class ChannelService(BaseService):
|
||||
mapper_name = "channel"
|
||||
|
||||
async def get_attachment_folder(self, channel_uid,ensure=False):
|
||||
path = pathlib.Path(f"./drive/{channel_uid}/attachments")
|
||||
if ensure:
|
||||
path.mkdir(
|
||||
parents=True, exist_ok=True
|
||||
)
|
||||
return path
|
||||
|
||||
async def get(self, uid=None, **kwargs):
|
||||
if uid:
|
||||
kwargs["uid"] = uid
|
||||
|
25
src/snek/service/channel_attachment.py
Normal file
25
src/snek/service/channel_attachment.py
Normal file
@ -0,0 +1,25 @@
|
||||
from snek.system.service import BaseService
|
||||
import urllib.parse
|
||||
import pathlib
|
||||
import mimetypes
|
||||
import uuid
|
||||
|
||||
class ChannelAttachmentService(BaseService):
|
||||
mapper_name="channel_attachment"
|
||||
|
||||
async def create_file(self, channel_uid, user_uid, name):
|
||||
attachment = await self.new()
|
||||
attachment["channel_uid"] = channel_uid
|
||||
attachment['user_uid'] = user_uid
|
||||
attachment["name"] = name
|
||||
attachment["mime_type"] = mimetypes.guess_type(name)[0]
|
||||
attachment['resource_type'] = "file"
|
||||
real_file_name = f"{attachment['uid']}-{name}"
|
||||
attachment["relative_url"] = urllib.parse.quote(f"{attachment['uid']}/{name}")
|
||||
attachment_folder = await self.services.channel.get_attachment_folder(channel_uid)
|
||||
attachment_path = attachment_folder.joinpath(real_file_name)
|
||||
attachment["path"] = str(attachment_path)
|
||||
if await self.save(attachment):
|
||||
return attachment
|
||||
raise Exception(f"Failed to create channel attachment: {attachment.errors}.")
|
||||
|
@ -21,13 +21,13 @@ class UploadButtonElement extends HTMLElement {
|
||||
|
||||
const files = fileInput.files;
|
||||
const formData = new FormData();
|
||||
formData.append('channel_uid', this.channelUid);
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
formData.append('files[]', files[i]);
|
||||
}
|
||||
|
||||
const request = new XMLHttpRequest();
|
||||
request.open('POST', '/drive.bin', true);
|
||||
|
||||
request.responseType = 'json';
|
||||
request.open('POST', `/channel/${this.channelUid}/attachment.bin`, true);
|
||||
|
||||
request.upload.onprogress = function (event) {
|
||||
if (event.lengthComputable) {
|
||||
@ -35,9 +35,10 @@ class UploadButtonElement extends HTMLElement {
|
||||
uploadButton.innerText = `${Math.round(percentComplete)}%`;
|
||||
}
|
||||
};
|
||||
|
||||
const me = this
|
||||
request.onload = function () {
|
||||
if (request.status === 200) {
|
||||
me.dispatchEvent(new CustomEvent('uploaded', { detail: request.response }));
|
||||
uploadButton.innerHTML = '📤';
|
||||
} else {
|
||||
alert('Upload failed');
|
||||
|
@ -47,6 +47,11 @@
|
||||
document.querySelector("upload-button").addEventListener("upload",function(e){
|
||||
getInputField().focus();
|
||||
})
|
||||
document.querySelector("upload-button").addEventListener("uploaded",function(e){
|
||||
e.detail.files.forEach((file)=>{
|
||||
app.rpc.sendMessage(channelUid,``)
|
||||
})
|
||||
})
|
||||
textBox.addEventListener("paste", async (e) => {
|
||||
try {
|
||||
const clipboardItems = await navigator.clipboard.read();
|
||||
|
53
src/snek/view/channel.py
Normal file
53
src/snek/view/channel.py
Normal file
@ -0,0 +1,53 @@
|
||||
from snek.system.view import BaseView
|
||||
import aiofiles
|
||||
from aiohttp import web
|
||||
import pathlib
|
||||
|
||||
class ChannelAttachmentView(BaseView):
|
||||
|
||||
async def get(self):
|
||||
relative_path = self.request.match_info.get("relative_url")
|
||||
channel_attachment = await self.services.channel_attachment.get(relative_url=relative_path)
|
||||
response = web.FileResponse(channel_attachment["path"])
|
||||
response.headers["Cache-Control"] = f"public, max-age={1337*420}"
|
||||
response.headers["Content-Disposition"] = (
|
||||
f'attachment; filename="{channel_attachment["name"]}"'
|
||||
)
|
||||
return response
|
||||
|
||||
async def post(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()
|
||||
|
||||
reader = await self.request.multipart()
|
||||
attachments = []
|
||||
|
||||
while field := await reader.next():
|
||||
|
||||
filename = field.filename
|
||||
if not filename:
|
||||
continue
|
||||
|
||||
attachment = await self.services.channel_attachment.create_file(
|
||||
channel_uid=channel_uid, name=filename,user_uid=user_uid
|
||||
)
|
||||
|
||||
attachments.append(attachment)
|
||||
pathlib.Path(attachment['path']).parent.mkdir(parents=True, exist_ok=True)
|
||||
async with aiofiles.open(attachment['path'], "wb") as f:
|
||||
while chunk := await field.read_chunk():
|
||||
await f.write(chunk)
|
||||
|
||||
return web.json_response(
|
||||
{
|
||||
"message": "Files uploaded successfully",
|
||||
"files": [attachment.record for attachment in attachments],
|
||||
"channel_uid": channel_uid,
|
||||
}
|
||||
)
|
Loading…
Reference in New Issue
Block a user