commit
6300e5a2e7
6 changed files with 2073 additions and 0 deletions
-
162.gitignore
-
165main.py
-
45ping_checker.py
-
1677poetry.lock
-
18pyproject.toml
-
6readme.md
@ -0,0 +1,162 @@ |
|||||
|
# Byte-compiled / optimized / DLL files |
||||
|
__pycache__/ |
||||
|
*.py[cod] |
||||
|
*$py.class |
||||
|
|
||||
|
# C extensions |
||||
|
*.so |
||||
|
|
||||
|
# Distribution / packaging |
||||
|
.Python |
||||
|
build/ |
||||
|
develop-eggs/ |
||||
|
dist/ |
||||
|
downloads/ |
||||
|
eggs/ |
||||
|
.eggs/ |
||||
|
lib/ |
||||
|
lib64/ |
||||
|
parts/ |
||||
|
sdist/ |
||||
|
var/ |
||||
|
wheels/ |
||||
|
share/python-wheels/ |
||||
|
*.egg-info/ |
||||
|
.installed.cfg |
||||
|
*.egg |
||||
|
MANIFEST |
||||
|
|
||||
|
# PyInstaller |
||||
|
# Usually these files are written by a python script from a template |
||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it. |
||||
|
*.manifest |
||||
|
*.spec |
||||
|
|
||||
|
# Installer logs |
||||
|
pip-log.txt |
||||
|
pip-delete-this-directory.txt |
||||
|
|
||||
|
# Unit test / coverage reports |
||||
|
htmlcov/ |
||||
|
.tox/ |
||||
|
.nox/ |
||||
|
.coverage |
||||
|
.coverage.* |
||||
|
.cache |
||||
|
nosetests.xml |
||||
|
coverage.xml |
||||
|
*.cover |
||||
|
*.py,cover |
||||
|
.hypothesis/ |
||||
|
.pytest_cache/ |
||||
|
cover/ |
||||
|
|
||||
|
# Translations |
||||
|
*.mo |
||||
|
*.pot |
||||
|
|
||||
|
# Django stuff: |
||||
|
*.log |
||||
|
local_settings.py |
||||
|
db.sqlite3 |
||||
|
db.sqlite3-journal |
||||
|
|
||||
|
# Flask stuff: |
||||
|
instance/ |
||||
|
.webassets-cache |
||||
|
|
||||
|
# Scrapy stuff: |
||||
|
.scrapy |
||||
|
|
||||
|
# Sphinx documentation |
||||
|
docs/_build/ |
||||
|
|
||||
|
# PyBuilder |
||||
|
.pybuilder/ |
||||
|
target/ |
||||
|
|
||||
|
# Jupyter Notebook |
||||
|
.ipynb_checkpoints |
||||
|
|
||||
|
# IPython |
||||
|
profile_default/ |
||||
|
ipython_config.py |
||||
|
|
||||
|
# pyenv |
||||
|
# For a library or package, you might want to ignore these files since the code is |
||||
|
# intended to run in multiple environments; otherwise, check them in: |
||||
|
# .python-version |
||||
|
|
||||
|
# pipenv |
||||
|
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. |
||||
|
# However, in case of collaboration, if having platform-specific dependencies or dependencies |
||||
|
# having no cross-platform support, pipenv may install dependencies that don't work, or not |
||||
|
# install all needed dependencies. |
||||
|
#Pipfile.lock |
||||
|
|
||||
|
# poetry |
||||
|
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. |
||||
|
# This is especially recommended for binary packages to ensure reproducibility, and is more |
||||
|
# commonly ignored for libraries. |
||||
|
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control |
||||
|
#poetry.lock |
||||
|
|
||||
|
# pdm |
||||
|
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. |
||||
|
#pdm.lock |
||||
|
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it |
||||
|
# in version control. |
||||
|
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control |
||||
|
.pdm.toml |
||||
|
.pdm-python |
||||
|
.pdm-build/ |
||||
|
|
||||
|
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm |
||||
|
__pypackages__/ |
||||
|
|
||||
|
# Celery stuff |
||||
|
celerybeat-schedule |
||||
|
celerybeat.pid |
||||
|
|
||||
|
# SageMath parsed files |
||||
|
*.sage.py |
||||
|
|
||||
|
# Environments |
||||
|
.env |
||||
|
.venv |
||||
|
env/ |
||||
|
venv/ |
||||
|
ENV/ |
||||
|
env.bak/ |
||||
|
venv.bak/ |
||||
|
|
||||
|
# Spyder project settings |
||||
|
.spyderproject |
||||
|
.spyproject |
||||
|
|
||||
|
# Rope project settings |
||||
|
.ropeproject |
||||
|
|
||||
|
# mkdocs documentation |
||||
|
/site |
||||
|
|
||||
|
# mypy |
||||
|
.mypy_cache/ |
||||
|
.dmypy.json |
||||
|
dmypy.json |
||||
|
|
||||
|
# Pyre type checker |
||||
|
.pyre/ |
||||
|
|
||||
|
# pytype static type analyzer |
||||
|
.pytype/ |
||||
|
|
||||
|
# Cython debug symbols |
||||
|
cython_debug/ |
||||
|
|
||||
|
# PyCharm |
||||
|
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can |
||||
|
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore |
||||
|
# and can be added to the global gitignore or merged into this file. For a more nuclear |
||||
|
# option (not recommended) you can uncomment the following to ignore the entire idea folder. |
||||
|
#.idea/ |
@ -0,0 +1,165 @@ |
|||||
|
from sqlalchemy import create_engine, Column, MetaData, ForeignKey, insert, func |
||||
|
import datetime |
||||
|
from contextlib import asynccontextmanager |
||||
|
from pydantic import BaseModel, Field |
||||
|
from typing import Sequence |
||||
|
from dotenv import load_dotenv |
||||
|
import asyncio |
||||
|
import os |
||||
|
from clickhouse_sqlalchemy import ( |
||||
|
Table, make_session, get_declarative_base, types, engines, select |
||||
|
) |
||||
|
from fastapi import FastAPI |
||||
|
load_dotenv() |
||||
|
uri = os.getenv("DATABASE_URL") |
||||
|
|
||||
|
engine = create_engine(uri) |
||||
|
session = make_session(engine) |
||||
|
metadata = MetaData() |
||||
|
|
||||
|
Base = get_declarative_base(metadata=metadata) |
||||
|
|
||||
|
|
||||
|
class Hosts(Base): |
||||
|
__tablename__ = "hosts" |
||||
|
url = Column(types.String, unique=False,primary_key=True) |
||||
|
delay = Column(types.Int16, default=60) |
||||
|
description = Column(types.String, nullable=True) |
||||
|
is_alive = Column(types.Boolean, default=False) |
||||
|
last_seen = Column(types.DateTime, server_default=func.now()) |
||||
|
last_alive = Column(types.DateTime, server_default=func.now()) |
||||
|
__table_args__ = ( |
||||
|
engines.Memory(), |
||||
|
) |
||||
|
|
||||
|
class HostBase(BaseModel): |
||||
|
url: str |
||||
|
|
||||
|
class HostDescription(BaseModel): |
||||
|
description: str | None |
||||
|
delay: int | None |
||||
|
|
||||
|
|
||||
|
class HostLive(BaseModel): |
||||
|
is_alive: bool | None |
||||
|
last_seen: datetime.datetime | None |
||||
|
last_alive: datetime.datetime | None |
||||
|
|
||||
|
class HostCreate(HostBase, HostDescription): |
||||
|
pass |
||||
|
class HostSchema(HostBase,HostDescription, HostLive): |
||||
|
pass |
||||
|
|
||||
|
|
||||
|
|
||||
|
async def get_hosts(): |
||||
|
return session.query(Hosts).all() |
||||
|
|
||||
|
async def get_host(host_url:str): |
||||
|
if host:=session.get(Hosts, host_url): |
||||
|
return host |
||||
|
return None |
||||
|
|
||||
|
async def insert_host(host: HostCreate): |
||||
|
data= Hosts( |
||||
|
**host.dict() |
||||
|
) |
||||
|
session.add(data) |
||||
|
session.commit() |
||||
|
return data |
||||
|
|
||||
|
async def remove_host(host_url: int): |
||||
|
data = session.get(Hosts, host_url) |
||||
|
if data: |
||||
|
session.delete(data) |
||||
|
session.commit() |
||||
|
return data |
||||
|
return None |
||||
|
|
||||
|
async def update_host_description(host_url: str, update_date: HostDescription): |
||||
|
data = session.get(Hosts, host_url) |
||||
|
if data: |
||||
|
if update_date.description: |
||||
|
data.description = update_date.description |
||||
|
if update_date.delay: |
||||
|
data.delay = update_date.delay |
||||
|
session.add(data) |
||||
|
session.commit() |
||||
|
return data |
||||
|
return None |
||||
|
|
||||
|
async def update_host_live(host_url: str, is_alive: bool): |
||||
|
data = session.get(Hosts, host_url) |
||||
|
if data: |
||||
|
data.is_alive = is_alive |
||||
|
data.last_alive =datetime.datetime.now() |
||||
|
if is_alive: |
||||
|
data.last_seen =datetime.datetime.now() |
||||
|
session.add(data) |
||||
|
session.commit() |
||||
|
return data |
||||
|
return None |
||||
|
|
||||
|
async def check_alive(url: str): |
||||
|
from ping_checker import sock_ch |
||||
|
is_alive = sock_ch(url) |
||||
|
await update_host_live(url, is_alive) |
||||
|
|
||||
|
async def delayed_alive(url:str, timeout:int): |
||||
|
await asyncio.sleep(timeout) |
||||
|
host =await get_host(url) |
||||
|
if host: |
||||
|
await check_alive(url) |
||||
|
return asyncio.create_task(delayed_alive(url, host.delay)) |
||||
|
|
||||
|
class BackgroundTask: |
||||
|
def __init__(self): |
||||
|
pass |
||||
|
async def my_task(self): |
||||
|
hosts = await get_hosts() |
||||
|
for host in hosts: |
||||
|
asyncio.create_task(delayed_alive(host.url, 0)) |
||||
|
|
||||
|
bgtask = BackgroundTask() |
||||
|
|
||||
|
|
||||
|
@asynccontextmanager |
||||
|
async def lifespan(app: FastAPI): |
||||
|
# Load the ML model |
||||
|
asyncio.create_task(bgtask.my_task()) |
||||
|
yield |
||||
|
# Clean up the ML models and release the resources |
||||
|
tasks = asyncio.all_tasks() |
||||
|
# cancel all tasks |
||||
|
for task in tasks: |
||||
|
# request the task cancel |
||||
|
task.cancel() |
||||
|
|
||||
|
|
||||
|
|
||||
|
app = FastAPI(lifespan=lifespan) |
||||
|
|
||||
|
|
||||
|
@app.get("/") |
||||
|
async def show_hosts() -> Sequence[HostSchema]: |
||||
|
data = await get_hosts() |
||||
|
return data |
||||
|
|
||||
|
@app.post("/") |
||||
|
async def add_host(host: HostCreate) ->HostSchema: |
||||
|
asyncio.create_task(delayed_alive(host.url, host.delay)) |
||||
|
return await insert_host(host) |
||||
|
|
||||
|
@app.delete("/") |
||||
|
async def delete_host(host_url: str) ->HostSchema | None: |
||||
|
return await remove_host(host_url) |
||||
|
|
||||
|
@app.put("/") |
||||
|
async def update_description(host_url: str, update_date: HostDescription)->HostSchema: |
||||
|
return await update_host_description(host_url, update_date) |
||||
|
|
||||
|
if __name__ == "__main__": |
||||
|
import uvicorn |
||||
|
Base.metadata.create_all(engine, checkfirst=True) |
||||
|
port = os.getenv("PORT") or 4004 |
||||
|
uvicorn.run(app=app, host="0.0.0.0", port=int(port)) |
@ -0,0 +1,45 @@ |
|||||
|
import socket |
||||
|
import ssl |
||||
|
from urllib.parse import urlparse |
||||
|
|
||||
|
|
||||
|
def port_get(url: str) -> int: |
||||
|
if ":" in urlparse(url).netloc: |
||||
|
return int(str(urlparse(url).netloc).split(":")[-1]) |
||||
|
else: |
||||
|
return 80 if urlparse(url).scheme == "http" else 443 |
||||
|
|
||||
|
|
||||
|
def chunk_url(url: str) -> tuple: |
||||
|
key = f'?{url.split("/")[-1].split("?")[-1]}' if "?" in url else "" |
||||
|
host = urlparse(url).hostname |
||||
|
path = f'{urlparse(url).path}{key}' |
||||
|
return host, path |
||||
|
|
||||
|
|
||||
|
def sock_ch(url: str) -> bool: |
||||
|
port = port_get(url) |
||||
|
host, path = chunk_url(url) |
||||
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
||||
|
sock.settimeout(5) |
||||
|
try: |
||||
|
sock.connect((host, port)) |
||||
|
|
||||
|
if port == 443: |
||||
|
context = ssl.create_default_context() |
||||
|
sock = context.wrap_socket(sock, server_hostname=host) |
||||
|
|
||||
|
sock.sendall(f"GET {path} HTTP/1.1\r\nHost:{host}\r\n\r\n".encode()) |
||||
|
|
||||
|
data = sock.recv(1024) |
||||
|
sock.close() |
||||
|
|
||||
|
if data: |
||||
|
return True |
||||
|
return False |
||||
|
except Exception: |
||||
|
sock.close() |
||||
|
return False |
||||
|
|
||||
|
URL = "https://api.jkhsakha.ru/info" |
||||
|
print(sock_ch(URL)) |
1677
poetry.lock
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,18 @@ |
|||||
|
[tool.poetry] |
||||
|
name = "ch-test" |
||||
|
version = "0.1.0" |
||||
|
description = "" |
||||
|
authors = ["VinokurovVE <coolday13@mail.ru>"] |
||||
|
readme = "README.md" |
||||
|
|
||||
|
[tool.poetry.dependencies] |
||||
|
python = "^3.11" |
||||
|
uvicorn = "^0.30.6" |
||||
|
fastapi = {extras = ["standard"], version = "^0.112.2"} |
||||
|
clickhouse-sqlalchemy = "^0.3.2" |
||||
|
python-dotenv = "^1.0.1" |
||||
|
|
||||
|
|
||||
|
[build-system] |
||||
|
requires = ["poetry-core"] |
||||
|
build-backend = "poetry.core.masonry.api" |
@ -0,0 +1,6 @@ |
|||||
|
добавить в проект файл .env |
||||
|
PORT = 4004 |
||||
|
DATABASE_URL ="" |
||||
|
|
||||
|
Запуск производиться с |
||||
|
poetry run .\main.py |
Write
Preview
Loading…
Cancel
Save
Reference in new issue