diff --git a/Makefile b/Makefile index 852efd4..9b60f5a 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,6 @@ serve: run run: .venv/bin/snek serve - #$(GUNICORN) -w $(GUNICORN_WORKERS) -k aiohttp.worker.GunicornWebWorker snek.gunicorn:app --bind 0.0.0.0:$(PORT) --reload install: ubuntu python3.12 -m venv .venv diff --git a/pyproject.toml b/pyproject.toml index 6cb0070..00a4edb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,7 +18,8 @@ dependencies = [ "lxml", "IPython", "shed", - "app @ git+https://retoor.molodetz.nl/retoor/app", + "app @ git+https://retoor.molodetz.nl/retoor/app.git", + "zhurnal @git+https://retoor.molodetz.nl/retoor/zhurnal.git", "beautifulsoup4", "gunicorn", "imgkit", diff --git a/src/snek/__main__.py b/src/snek/__main__.py index 0f06499..360bf5a 100644 --- a/src/snek/__main__.py +++ b/src/snek/__main__.py @@ -1,24 +1,45 @@ import click +import uvloop from aiohttp import web import asyncio from snek.app import Application from IPython import start_ipython +import sqlite3 +import pathlib +import shutil @click.group() def cli(): pass +@cli.command() +@click.option('--db_path',default="snek.db", help='Database to initialize if not exists.') +@click.option('--source',default=None, help='Database to initialize if not exists.') +def init(db_path,source): + if source and pathlib.Path(source).exists(): + print(f"Copying {source} to {db_path}") + shutil.copy2(source,db_path) + print("Database initialized.") + return + + if pathlib.Path(db_path).exists(): + return + print(f"Initializing database at {db_path}") + db = sqlite3.connect(db_path) + db.cursor().executescript( + pathlib.Path(__file__).parent.joinpath("schema.sql").read_text() + ) + db.commit() + db.close() + print("Database initialized.") + @cli.command() @click.option('--port', default=8081, show_default=True, help='Port to run the application on') @click.option('--host', default='0.0.0.0', show_default=True, help='Host to run the application on') @click.option('--db_path', default='snek.db', show_default=True, help='Database path for the application') def serve(port, host, db_path): - try: - import uvloop - asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) - except ImportError: - print("uvloop not installed, using default event loop.") - + #init(db_path) + #asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) web.run_app( Application(db_path=f"sqlite:///{db_path}"), port=port, host=host ) diff --git a/src/snek/app.py b/src/snek/app.py index a8ed41c..bbf6539 100644 --- a/src/snek/app.py +++ b/src/snek/app.py @@ -3,9 +3,9 @@ import logging import pathlib import time import uuid - +from snek import snode from snek.view.threads import ThreadsView - +import json logging.basicConfig(level=logging.DEBUG) from concurrent.futures import ThreadPoolExecutor @@ -57,7 +57,6 @@ from snek.view.web import WebView from snek.view.channel import ChannelAttachmentView from snek.webdav import WebdavApplication from snek.sgit import GitApplication - SESSION_KEY = b"c79a0c5fda4b424189c427d28c9f7c34" @@ -99,18 +98,26 @@ class Application(BaseApplication): self.ssh_host = "0.0.0.0" self.ssh_port = 2242 self.setup_router() - self.ssh_server = None + self.ssh_server = None + self.sync_service = None self.executor = None self.cache = Cache(self) self.services = get_services(app=self) self.mappers = get_mappers(app=self) + self.broadcast_service = None self.on_startup.append(self.start_ssh_server) self.on_startup.append(self.prepare_asyncio) self.on_startup.append(self.prepare_database) + + + async def snode_sync(self, app): + self.sync_service = asyncio.create_task(snode.sync_service(app)) + async def start_ssh_server(self, app): app.ssh_server = await start_ssh_server(app,app.ssh_host,app.ssh_port) - asyncio.create_task(app.ssh_server.wait_closed()) + if app.ssh_server: + asyncio.create_task(app.ssh_server.wait_closed()) async def prepare_asyncio(self, app): # app.loop = asyncio.get_running_loop() diff --git a/src/snek/service/drive_item.py b/src/snek/service/drive_item.py index ce747c1..7a0c59a 100644 --- a/src/snek/service/drive_item.py +++ b/src/snek/service/drive_item.py @@ -16,4 +16,5 @@ class DriveItemService(BaseService): if await self.save(model): return model errors = await model.errors + print("XXXXXXXXXX") raise Exception(f"Failed to create drive item: {errors}.") diff --git a/src/snek/service/socket.py b/src/snek/service/socket.py index 72d9b72..eb40234 100644 --- a/src/snek/service/socket.py +++ b/src/snek/service/socket.py @@ -1,6 +1,7 @@ from snek.model.user import UserModel from snek.system.service import BaseService - +from datetime import datetime +import json class SocketService(BaseService): @@ -10,6 +11,7 @@ class SocketService(BaseService): self.is_connected = True self.user = user + async def send_json(self, data): if not self.is_connected: return False @@ -33,7 +35,8 @@ class SocketService(BaseService): self.sockets = set() self.users = {} self.subscriptions = {} - + self.last_update = str(datetime.now()) + async def add(self, ws, user_uid): s = self.Socket(ws, await self.app.services.user.get(uid=user_uid)) self.sockets.add(s) @@ -54,7 +57,11 @@ class SocketService(BaseService): count += 1 return count + async def broadcast(self, channel_uid, message): + await self._broadcast(channel_uid, message) + + async def _broadcast(self, channel_uid, message): try: async for user_uid in self.services.channel_member.get_user_uids( channel_uid diff --git a/src/snek/sgit.py b/src/snek/sgit.py index 65e18ac..3cbdcf6 100644 --- a/src/snek/sgit.py +++ b/src/snek/sgit.py @@ -1,7 +1,7 @@ import os import aiohttp from aiohttp import web -import git + import shutil import json import tempfile @@ -14,6 +14,8 @@ logger = logging.getLogger('git_server') class GitApplication(web.Application): def __init__(self, parent=None): + #import git + #globals()['git'] = git self.parent = parent super().__init__(client_max_size=1024*1024*1024*5) self.add_routes([ diff --git a/src/snek/sssh.py b/src/snek/sssh.py index 848b2f9..6331efa 100644 --- a/src/snek/sssh.py +++ b/src/snek/sssh.py @@ -4,7 +4,6 @@ from pathlib import Path global _app - def set_app(app): global _app _app = app @@ -12,19 +11,11 @@ def set_app(app): def get_app(): return _app -logging.basicConfig( - level=logging.DEBUG, - format="%(asctime)s - %(levelname)s - %(message)s", - handlers=[ - logging.FileHandler("sftp_server.log"), - logging.StreamHandler() - ] -) logger = logging.getLogger(__name__) roots = {} -class MySFTPServer(asyncssh.SFTPServer): +class SFTPServer(asyncssh.SFTPServer): def __init__(self, chan: asyncssh.SSHServerChannel): self.root = get_app().services.user.get_home_folder_by_username( @@ -35,29 +26,24 @@ class MySFTPServer(asyncssh.SFTPServer): super().__init__(chan, chroot=self.root) def map_path(self, path): - - # Map client path to local filesystem path mapped_path = Path(self.root).joinpath(path.lstrip(b"/").decode()) - #Path(mapped_path).mkdir(exist_ok=True) - print(mapped_path) logger.debug(f"Mapping client path {path} to {mapped_path}") return str(mapped_path).encode() -class MySSHServer(asyncssh.SSHServer): +class SSHServer(asyncssh.SSHServer): def password_auth_supported(self): return True def validate_password(self, username, password): - # Simple in-memory user database - logger.debug(f"Validating credentials for user {username}") - return get_app().services.user.authenticate_sync(username,password) - + result = get_app().services.user.authenticate_sync(username,password) + logger.info(f"Validating credentials for user {username}: {result}") + return result async def start_ssh_server(app,host,port): set_app(app) logger.info("Starting SFTP server setup") - # Server host key (generate or load one) + host_key_path = Path("drive") / ".ssh" / "sftp_server_key" host_key_path.parent.mkdir(exist_ok=True) try: @@ -72,20 +58,19 @@ async def start_ssh_server(app,host,port): logger.error(f"Failed to generate or load host key: {e}") raise - # Start the SSH server with SFTP support - logger.info("Starting SFTP server on localhost:8022") + logger.info(f"Starting SFTP server on 127.0.0.1:{port}") try: x = await asyncssh.listen( host=host, port=port, #process_factory=handle_client, server_host_keys=[key], - server_factory=MySSHServer, - sftp_factory=MySFTPServer + server_factory=SSHServer, + sftp_factory=SFTPServer ) return x except Exception as e: - logger.error(f"Failed to start SFTP server: {e}") - raise + logger.warning(f"Failed to start SFTP server. Already running.") + pass diff --git a/src/snek/view/repository.py b/src/snek/view/repository.py index 0c19142..60fa1bb 100644 --- a/src/snek/view/repository.py +++ b/src/snek/view/repository.py @@ -6,7 +6,7 @@ import humanize from aiohttp import web from snek.system.view import BaseView import asyncio -from git import Repo +