Compare commits

..
Author SHA1 Message Date
retoor 557b34b71a Update. 2025-06-01 09:42:06 +02:00
retoor e0255b28ec Update. 2025-06-01 03:38:12 +02:00
retoor 69855fa118 Update. 2025-06-01 03:33:58 +02:00
retoor a07f2680d6 Update. 2025-06-01 03:24:14 +02:00
retoor d022cff499 Update. 2025-06-01 00:39:53 +02:00
retoor d4a480b5ea Update. 2025-06-01 00:38:22 +02:00
retoor 161ff392d7 Update. 2025-06-01 00:33:47 +02:00
retoor 4e72fbf84b Merge pull request 'Make database asnyc.' (#49) from feat/make-database-async into main
Reviewed-on: #49
2025-06-01 00:28:34 +02:00
6 changed files with 53 additions and 5 deletions
+1
View File
@@ -38,6 +38,7 @@ dependencies = [
"humanize", "humanize",
"Pillow", "Pillow",
"pillow-heif", "pillow-heif",
"IP2Location",
] ]
[tool.setuptools.packages.find] [tool.setuptools.packages.find]
BIN
View File
Binary file not shown.
+33 -3
View File
@@ -8,7 +8,7 @@ from snek import snode
from snek.view.threads import ThreadsView from snek.view.threads import ThreadsView
import json import json
logging.basicConfig(level=logging.DEBUG) logging.basicConfig(level=logging.DEBUG)
from ipaddress import ip_address
from concurrent.futures import ThreadPoolExecutor from concurrent.futures import ThreadPoolExecutor
from aiohttp import web from aiohttp import web
@@ -20,7 +20,7 @@ from aiohttp_session import (
from aiohttp_session.cookie_storage import EncryptedCookieStorage from aiohttp_session.cookie_storage import EncryptedCookieStorage
from app.app import Application as BaseApplication from app.app import Application as BaseApplication
from jinja2 import FileSystemLoader from jinja2 import FileSystemLoader
import IP2Location
from snek.sssh import start_ssh_server from snek.sssh import start_ssh_server
from snek.docs.app import Application as DocsApplication from snek.docs.app import Application as DocsApplication
from snek.mapper import get_mappers from snek.mapper import get_mappers
@@ -69,6 +69,31 @@ async def session_middleware(request, handler):
response = await handler(request) response = await handler(request)
return response return response
@web.middleware
async def ip2location_middleware(request, handler):
response = await handler(request)
return response
ip = request.headers.get("X-Forwarded-For", request.remote)
ipaddress = ip_address(ip)
if ipaddress.is_private:
return response
if not request.app.session.get("uid"):
return response
user = await request.app.services.user.get(uid=request.app.session.get("uid"))
if not user:
return response
location = request.app.ip2location.get(ip)
original_city = user['city']
if user['city'] != location.city:
user['country_long'] = location.country
user['country_short'] = locaion.country_short
user['city'] = location.city
user['region'] = location.region
user['latitude'] = location.latitude
user['longitude'] = location.longitude
user['ip'] = ip
await request.app.services.user.update(user)
return response
@web.middleware @web.middleware
async def trailing_slash_middleware(request, handler): async def trailing_slash_middleware(request, handler):
@@ -84,6 +109,7 @@ class Application(BaseApplication):
middlewares = [ middlewares = [
cors_middleware, cors_middleware,
web.normalize_path_middleware(merge_slashes=True), web.normalize_path_middleware(merge_slashes=True),
ip2location_middleware
] ]
self.template_path = pathlib.Path(__file__).parent.joinpath("templates") self.template_path = pathlib.Path(__file__).parent.joinpath("templates")
self.static_path = pathlib.Path(__file__).parent.joinpath("static") self.static_path = pathlib.Path(__file__).parent.joinpath("static")
@@ -111,11 +137,15 @@ class Application(BaseApplication):
self.broadcast_service = None self.broadcast_service = None
self.user_availability_service_task = None self.user_availability_service_task = None
base_path = pathlib.Path(__file__).parent
self.ip2location = IP2Location.IP2Location(base_path.joinpath("IP2LOCATION-LITE-DB11.BIN"))
self.on_startup.append(self.prepare_asyncio) self.on_startup.append(self.prepare_asyncio)
self.on_startup.append(self.start_user_availability_service) self.on_startup.append(self.start_user_availability_service)
self.on_startup.append(self.start_ssh_server) self.on_startup.append(self.start_ssh_server)
self.on_startup.append(self.prepare_database) self.on_startup.append(self.prepare_database)
@property @property
def uptime_seconds(self): def uptime_seconds(self):
return (datetime.now() - self.time_start).total_seconds() return (datetime.now() - self.time_start).total_seconds()
+8
View File
@@ -30,6 +30,14 @@ class UserModel(BaseModel):
last_ping = ModelField(name="last_ping", required=False, kind=str) last_ping = ModelField(name="last_ping", required=False, kind=str)
is_admin = ModelField(name="is_admin", required=False, kind=bool) is_admin = ModelField(name="is_admin", required=False, kind=bool)
country_short = ModelField(name="country_short", required=False, kind=str)
country_long = ModelField(name="country_long", required=False, kind=str)
city = ModelField(name="city", required=False, kind=str)
latitude = ModelField(name="latitude", required=False, kind=float)
longitude = ModelField(name="longitude", required=False, kind=float)
region = ModelField(name="region", required=False, kind=str)
ip = ModelField(name="ip", required=False, kind=str)
async def get_property(self, name): async def get_property(self, name):
prop = await self.app.services.user_property.find_one( prop = await self.app.services.user_property.find_one(
+8 -2
View File
@@ -12,7 +12,7 @@ class BaseMapper:
def __init__(self, app): def __init__(self, app):
self.app = app self.app = app
self.semaphore = asyncio.Semaphore(1)
self.default_limit = self.__class__.default_limit self.default_limit = self.__class__.default_limit
@property @property
@@ -24,7 +24,9 @@ class BaseMapper:
return asyncio.get_event_loop() return asyncio.get_event_loop()
async def run_in_executor(self, func, *args, **kwargs): async def run_in_executor(self, func, *args, **kwargs):
return await self.loop.run_in_executor(None, lambda: func(*args, **kwargs)) async with self.semaphore:
return func(*args, **kwargs)
#return await self.loop.run_in_executor(None, lambda: func(*args, **kwargs))
async def new(self): async def new(self):
return self.model_class(mapper=self, app=self.app) return self.model_class(mapper=self, app=self.app)
@@ -72,6 +74,10 @@ class BaseMapper:
for record in await self.run_in_executor(self.db.query,sql, *args): for record in await self.run_in_executor(self.db.query,sql, *args):
yield dict(record) yield dict(record)
async def update(self, model):
model.updated_at.update()
return await self.run_in_executor(self.table.update, model.record, ["uid"])
async def delete(self, **kwargs) -> int: async def delete(self, **kwargs) -> int:
if not kwargs or not isinstance(kwargs, dict): if not kwargs or not isinstance(kwargs, dict):
raise Exception("Can't execute delete with no filter.") raise Exception("Can't execute delete with no filter.")
+3
View File
@@ -26,6 +26,9 @@ class BaseService:
kwargs["uid"] = uid kwargs["uid"] = uid
return await self.count(**kwargs) > 0 return await self.count(**kwargs) > 0
async def update(self, model):
return await self.mapper.update(model)
async def count(self, **kwargs): async def count(self, **kwargs):
return await self.mapper.count(**kwargs) return await self.mapper.count(**kwargs)