Windows friendly solution.
This commit is contained in:
parent
ac2f68f93f
commit
a4bea94495
1
Makefile
1
Makefile
@ -20,7 +20,6 @@ serve: run
|
|||||||
|
|
||||||
run:
|
run:
|
||||||
.venv/bin/snek serve
|
.venv/bin/snek serve
|
||||||
#$(GUNICORN) -w $(GUNICORN_WORKERS) -k aiohttp.worker.GunicornWebWorker snek.gunicorn:app --bind 0.0.0.0:$(PORT) --reload
|
|
||||||
|
|
||||||
install: ubuntu
|
install: ubuntu
|
||||||
python3.12 -m venv .venv
|
python3.12 -m venv .venv
|
||||||
|
@ -18,7 +18,8 @@ dependencies = [
|
|||||||
"lxml",
|
"lxml",
|
||||||
"IPython",
|
"IPython",
|
||||||
"shed",
|
"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",
|
"beautifulsoup4",
|
||||||
"gunicorn",
|
"gunicorn",
|
||||||
"imgkit",
|
"imgkit",
|
||||||
|
@ -1,24 +1,45 @@
|
|||||||
import click
|
import click
|
||||||
|
import uvloop
|
||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
import asyncio
|
import asyncio
|
||||||
from snek.app import Application
|
from snek.app import Application
|
||||||
from IPython import start_ipython
|
from IPython import start_ipython
|
||||||
|
import sqlite3
|
||||||
|
import pathlib
|
||||||
|
import shutil
|
||||||
|
|
||||||
@click.group()
|
@click.group()
|
||||||
def cli():
|
def cli():
|
||||||
pass
|
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()
|
@cli.command()
|
||||||
@click.option('--port', default=8081, show_default=True, help='Port to run the application on')
|
@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('--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')
|
@click.option('--db_path', default='snek.db', show_default=True, help='Database path for the application')
|
||||||
def serve(port, host, db_path):
|
def serve(port, host, db_path):
|
||||||
try:
|
#init(db_path)
|
||||||
import uvloop
|
#asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
|
||||||
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
|
|
||||||
except ImportError:
|
|
||||||
print("uvloop not installed, using default event loop.")
|
|
||||||
|
|
||||||
web.run_app(
|
web.run_app(
|
||||||
Application(db_path=f"sqlite:///{db_path}"), port=port, host=host
|
Application(db_path=f"sqlite:///{db_path}"), port=port, host=host
|
||||||
)
|
)
|
||||||
|
@ -3,9 +3,9 @@ import logging
|
|||||||
import pathlib
|
import pathlib
|
||||||
import time
|
import time
|
||||||
import uuid
|
import uuid
|
||||||
|
from snek import snode
|
||||||
from snek.view.threads import ThreadsView
|
from snek.view.threads import ThreadsView
|
||||||
|
import json
|
||||||
logging.basicConfig(level=logging.DEBUG)
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
|
||||||
from concurrent.futures import ThreadPoolExecutor
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
@ -57,7 +57,6 @@ from snek.view.web import WebView
|
|||||||
from snek.view.channel import ChannelAttachmentView
|
from snek.view.channel import ChannelAttachmentView
|
||||||
from snek.webdav import WebdavApplication
|
from snek.webdav import WebdavApplication
|
||||||
from snek.sgit import GitApplication
|
from snek.sgit import GitApplication
|
||||||
|
|
||||||
SESSION_KEY = b"c79a0c5fda4b424189c427d28c9f7c34"
|
SESSION_KEY = b"c79a0c5fda4b424189c427d28c9f7c34"
|
||||||
|
|
||||||
|
|
||||||
@ -99,18 +98,26 @@ class Application(BaseApplication):
|
|||||||
self.ssh_host = "0.0.0.0"
|
self.ssh_host = "0.0.0.0"
|
||||||
self.ssh_port = 2242
|
self.ssh_port = 2242
|
||||||
self.setup_router()
|
self.setup_router()
|
||||||
self.ssh_server = None
|
self.ssh_server = None
|
||||||
|
self.sync_service = None
|
||||||
self.executor = None
|
self.executor = None
|
||||||
self.cache = Cache(self)
|
self.cache = Cache(self)
|
||||||
self.services = get_services(app=self)
|
self.services = get_services(app=self)
|
||||||
self.mappers = get_mappers(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.start_ssh_server)
|
||||||
self.on_startup.append(self.prepare_asyncio)
|
self.on_startup.append(self.prepare_asyncio)
|
||||||
self.on_startup.append(self.prepare_database)
|
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):
|
async def start_ssh_server(self, app):
|
||||||
app.ssh_server = await start_ssh_server(app,app.ssh_host,app.ssh_port)
|
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):
|
async def prepare_asyncio(self, app):
|
||||||
# app.loop = asyncio.get_running_loop()
|
# app.loop = asyncio.get_running_loop()
|
||||||
|
@ -16,4 +16,5 @@ class DriveItemService(BaseService):
|
|||||||
if await self.save(model):
|
if await self.save(model):
|
||||||
return model
|
return model
|
||||||
errors = await model.errors
|
errors = await model.errors
|
||||||
|
print("XXXXXXXXXX")
|
||||||
raise Exception(f"Failed to create drive item: {errors}.")
|
raise Exception(f"Failed to create drive item: {errors}.")
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from snek.model.user import UserModel
|
from snek.model.user import UserModel
|
||||||
from snek.system.service import BaseService
|
from snek.system.service import BaseService
|
||||||
|
from datetime import datetime
|
||||||
|
import json
|
||||||
|
|
||||||
class SocketService(BaseService):
|
class SocketService(BaseService):
|
||||||
|
|
||||||
@ -10,6 +11,7 @@ class SocketService(BaseService):
|
|||||||
self.is_connected = True
|
self.is_connected = True
|
||||||
self.user = user
|
self.user = user
|
||||||
|
|
||||||
|
|
||||||
async def send_json(self, data):
|
async def send_json(self, data):
|
||||||
if not self.is_connected:
|
if not self.is_connected:
|
||||||
return False
|
return False
|
||||||
@ -33,7 +35,8 @@ class SocketService(BaseService):
|
|||||||
self.sockets = set()
|
self.sockets = set()
|
||||||
self.users = {}
|
self.users = {}
|
||||||
self.subscriptions = {}
|
self.subscriptions = {}
|
||||||
|
self.last_update = str(datetime.now())
|
||||||
|
|
||||||
async def add(self, ws, user_uid):
|
async def add(self, ws, user_uid):
|
||||||
s = self.Socket(ws, await self.app.services.user.get(uid=user_uid))
|
s = self.Socket(ws, await self.app.services.user.get(uid=user_uid))
|
||||||
self.sockets.add(s)
|
self.sockets.add(s)
|
||||||
@ -54,7 +57,11 @@ class SocketService(BaseService):
|
|||||||
count += 1
|
count += 1
|
||||||
return count
|
return count
|
||||||
|
|
||||||
|
|
||||||
async def broadcast(self, channel_uid, message):
|
async def broadcast(self, channel_uid, message):
|
||||||
|
await self._broadcast(channel_uid, message)
|
||||||
|
|
||||||
|
async def _broadcast(self, channel_uid, message):
|
||||||
try:
|
try:
|
||||||
async for user_uid in self.services.channel_member.get_user_uids(
|
async for user_uid in self.services.channel_member.get_user_uids(
|
||||||
channel_uid
|
channel_uid
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import os
|
import os
|
||||||
import aiohttp
|
import aiohttp
|
||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
import git
|
|
||||||
import shutil
|
import shutil
|
||||||
import json
|
import json
|
||||||
import tempfile
|
import tempfile
|
||||||
@ -14,6 +14,8 @@ logger = logging.getLogger('git_server')
|
|||||||
|
|
||||||
class GitApplication(web.Application):
|
class GitApplication(web.Application):
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
|
#import git
|
||||||
|
#globals()['git'] = git
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
super().__init__(client_max_size=1024*1024*1024*5)
|
super().__init__(client_max_size=1024*1024*1024*5)
|
||||||
self.add_routes([
|
self.add_routes([
|
||||||
|
@ -4,7 +4,6 @@ from pathlib import Path
|
|||||||
|
|
||||||
global _app
|
global _app
|
||||||
|
|
||||||
|
|
||||||
def set_app(app):
|
def set_app(app):
|
||||||
global _app
|
global _app
|
||||||
_app = app
|
_app = app
|
||||||
@ -12,19 +11,11 @@ def set_app(app):
|
|||||||
def get_app():
|
def get_app():
|
||||||
return _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__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
roots = {}
|
roots = {}
|
||||||
|
|
||||||
class MySFTPServer(asyncssh.SFTPServer):
|
class SFTPServer(asyncssh.SFTPServer):
|
||||||
|
|
||||||
def __init__(self, chan: asyncssh.SSHServerChannel):
|
def __init__(self, chan: asyncssh.SSHServerChannel):
|
||||||
self.root = get_app().services.user.get_home_folder_by_username(
|
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)
|
super().__init__(chan, chroot=self.root)
|
||||||
|
|
||||||
def map_path(self, path):
|
def map_path(self, path):
|
||||||
|
|
||||||
# Map client path to local filesystem path
|
|
||||||
mapped_path = Path(self.root).joinpath(path.lstrip(b"/").decode())
|
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}")
|
logger.debug(f"Mapping client path {path} to {mapped_path}")
|
||||||
return str(mapped_path).encode()
|
return str(mapped_path).encode()
|
||||||
|
|
||||||
class MySSHServer(asyncssh.SSHServer):
|
class SSHServer(asyncssh.SSHServer):
|
||||||
def password_auth_supported(self):
|
def password_auth_supported(self):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def validate_password(self, username, password):
|
def validate_password(self, username, password):
|
||||||
# Simple in-memory user database
|
|
||||||
|
|
||||||
logger.debug(f"Validating credentials for user {username}")
|
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):
|
async def start_ssh_server(app,host,port):
|
||||||
set_app(app)
|
set_app(app)
|
||||||
logger.info("Starting SFTP server setup")
|
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 = Path("drive") / ".ssh" / "sftp_server_key"
|
||||||
host_key_path.parent.mkdir(exist_ok=True)
|
host_key_path.parent.mkdir(exist_ok=True)
|
||||||
try:
|
try:
|
||||||
@ -72,20 +58,19 @@ async def start_ssh_server(app,host,port):
|
|||||||
logger.error(f"Failed to generate or load host key: {e}")
|
logger.error(f"Failed to generate or load host key: {e}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
# Start the SSH server with SFTP support
|
logger.info(f"Starting SFTP server on 127.0.0.1:{port}")
|
||||||
logger.info("Starting SFTP server on localhost:8022")
|
|
||||||
try:
|
try:
|
||||||
x = await asyncssh.listen(
|
x = await asyncssh.listen(
|
||||||
host=host,
|
host=host,
|
||||||
port=port,
|
port=port,
|
||||||
#process_factory=handle_client,
|
#process_factory=handle_client,
|
||||||
server_host_keys=[key],
|
server_host_keys=[key],
|
||||||
server_factory=MySSHServer,
|
server_factory=SSHServer,
|
||||||
sftp_factory=MySFTPServer
|
sftp_factory=SFTPServer
|
||||||
)
|
)
|
||||||
return x
|
return x
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to start SFTP server: {e}")
|
logger.warning(f"Failed to start SFTP server. Already running.")
|
||||||
raise
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import humanize
|
|||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
from snek.system.view import BaseView
|
from snek.system.view import BaseView
|
||||||
import asyncio
|
import asyncio
|
||||||
from git import Repo
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user