from datetime import datetime from typing import List, Optional from sqlalchemy import create_engine, String, select, ForeignKey from sqlalchemy.orm import Session, DeclarativeBase, Mapped, mapped_column, relationship import strawberry import json import uvicorn from fastapi import FastAPI from strawberry.fastapi import GraphQLRouter from starlette.middleware.cors import CORSMiddleware from strawberry.scalars import JSON from xmlrpc.client import ServerProxy from config import Config def connect(): return create_engine(Config.db_connection_string, echo=True) def exec_function(function: str, **kwargs): proxy = ServerProxy(Config.enserver_url) result = proxy.__getattr__(function)(kwargs) return result @strawberry.type class UsersGQL: users: JSON class Base(DeclarativeBase): pass def tow(day: int, hour: int, minute: int): return minute + hour * 60 + day * 60 * 24 class User(Base): __tablename__ = 'users' id: Mapped[int] = mapped_column(primary_key=True) username: Mapped[str] passwd: Mapped[str] bndname: Mapped[str] newbnd: Mapped[bool] active: Mapped[bool] upstream: Mapped[bool] profiles: Mapped[List['Profile']] = relationship( back_populates='user', cascade='all, delete-orphan' ) income_branches: Mapped[List['IncomeBranch']] = relationship( back_populates='user', cascade='all, delete-orphan' ) schedule: Mapped[List['Schedule']] = relationship( back_populates='user', cascade='all, delete-orphan' ) queue: Mapped[List['Queue']] = relationship( back_populates='user', cascade='all, delete-orphan' ) def __repr__(self) -> str: return f'User(id={self.id!r}, username={self.username!r}, password={self.passwd!r}, newbnd={self.newbnd})' def to_dict(self) -> dict: return { 'id': self.id, 'username': self.username, 'bndname': self.bndname, 'newbnd': self.newbnd, 'active': self.active, 'upstream': self.upstream, 'profiles': [x.to_dict() for x in self.profiles], 'schedule': [x.to_dict() for x in self.schedule], 'queue': [x.to_dict() for x in self.queue], 'income_branches': [x.to_dict() for x in self.income_branches], } def is_active_now(self): if not len(self.schedule): return True dt = datetime.now() curr_tow = tow(dt.weekday(), dt.hour, dt.minute) for x in self.schedule: if (tow(x.day_start, x.hour_start, x.minute_start) <= curr_tow <= tow(x.day_end, x.hour_end, x.minute_end)): return True return False class Profile(Base): __tablename__ = 'profiles' id: Mapped[int] = mapped_column(primary_key=True) user_id: Mapped[int] = mapped_column(ForeignKey('users.id')) scheme: Mapped[str] branch: Mapped[str] = mapped_column(String, nullable=True) json: Mapped[str] = mapped_column(String, nullable=True) user: Mapped['User'] = relationship(back_populates='profiles') def to_dict(self) -> dict: return { 'id': self.id, 'scheme': self.scheme, 'branch': self.branch, 'json': self.json, } class IncomeBranch(Base): __tablename__ = 'income_branches' id: Mapped[int] = mapped_column(primary_key=True) user_id: Mapped[int] = mapped_column(ForeignKey('users.id')) scheme: Mapped[str] branch: Mapped[str] local_scheme: Mapped[str] user: Mapped['User'] = relationship(back_populates='income_branches') def to_dict(self) -> dict: return { 'id': self.id, 'scheme': self.scheme, 'branch': self.branch, 'local_scheme': self.local_scheme, } class Schedule(Base): __tablename__ = 'schedule' id: Mapped[int] = mapped_column(primary_key=True) user_id: Mapped[int] = mapped_column(ForeignKey('users.id')) day_start: Mapped[int] hour_start: Mapped[int] minute_start: Mapped[int] day_end: Mapped[int] hour_end: Mapped[int] minute_end: Mapped[int] user: Mapped['User'] = relationship(back_populates='schedule') def to_dict(self) -> dict: return { 'id': self.id, 'day_start': self.day_start, 'hour_start': self.hour_start, 'minute_start': self.minute_start, 'day_end': self.day_end, 'hour_end': self.hour_end, 'minute_end': self.minute_end, } class Queue(Base): __tablename__ = 'queue' id: Mapped[int] = mapped_column(primary_key=True) user_id: Mapped[int] = mapped_column(ForeignKey('users.id')) commit_id: Mapped[str] schema: Mapped[str] user: Mapped['User'] = relationship(back_populates='queue') def to_dict(self) -> dict: return { 'id': self.id, 'bnd': self.user.bndname, 'commit_id': self.commit_id, 'schema': self.schema, } class Schemas(Base): __tablename__ = 'schemas' id: Mapped[int] = mapped_column(primary_key=True) schema: Mapped[str] schema_type: Mapped[str] @strawberry.type class Query: @strawberry.field() def users(self) -> UsersGQL: sync_engine = connect() with Session(sync_engine) as session: stmt = select(User) return UsersGQL(users=[json.dumps(user.to_dict()) for user in session.scalars(stmt)]) @strawberry.field() def user(self, id: int) -> UsersGQL: sync_engine = connect() with Session(sync_engine) as session: return UsersGQL(users=json.dumps(session.query(User).filter(User.id == id).one_or_none().to_dict())) @strawberry.field() def queue(self) -> UsersGQL: sync_engine = connect() with Session(sync_engine) as session: stmt = select(Queue) return UsersGQL(users=[json.dumps(queue.to_dict()) for queue in session.scalars(stmt)]) @strawberry.type class Mutation: @strawberry.mutation def update_user(self, id_: int, bndname: str, newbnd: bool, active: bool, upstream: bool, passwd: str, username: str) -> bool: sync_engine = connect() with Session(sync_engine) as session: user = session.query(User).get(id_) user.username = username user.bndname = bndname user.newbnd = newbnd user.active = active user.upstream = upstream user.passwd = passwd session.commit() return True @strawberry.mutation def create_user(self, username: str, passwd: str) -> int: sync_engine = connect() with Session(sync_engine) as session: user = User(username=username, passwd=passwd, bndname=username, active=False, newbnd=True, upstream=False) session.add_all([user]) session.commit() return user.id @strawberry.mutation def update_profile(self, id_: int, scheme: str, json: str) -> bool: sync_engine = connect() with Session(sync_engine) as session: profile = session.query(Profile).get(id_) profile.scheme = scheme profile.json = json session.commit() return True @strawberry.mutation def create_profile(self, user_id: int, schema: str, branch: str, json: str) -> int: sync_engine = connect() with (Session(sync_engine) as session): user = session.query(User).get(user_id) if not user: return 0 profile = Profile(user_id=user_id, scheme=schema, branch=branch, json=json) session.add_all([profile]) session.commit() return profile.id @strawberry.mutation def update_schedule(self, id_: int, day_start: int, day_end: int, hour_start: int, hour_end: int, minute_start: int, minute_end: int) -> bool: sync_engine = connect() with Session(sync_engine) as session: schedule = session.query(Schedule).get(id_) schedule.day_start = day_start schedule.day_end = day_end schedule.hour_start = hour_start schedule.hour_end = hour_end schedule.minute_start = minute_start schedule.minute_end = minute_end session.commit() return True @strawberry.mutation def create_schedule(self, user_id: int, day_start: int, day_end: int, hour_start: int, hour_end: int, minute_start: int, minute_end: int) -> int: sync_engine = connect() with (Session(sync_engine) as session): user = session.query(User).get(user_id) if not user: return 0 schedule = Schedule(user_id=user_id, day_start=day_start, day_end=day_end, hour_start=hour_start, hour_end=hour_end, minute_start=minute_start, minute_end=minute_end) session.add_all([schedule]) session.commit() return schedule.id @strawberry.mutation def remove_user(self, id_: int) -> bool: sync_engine = connect() with (Session(sync_engine) as session): user = session.query(User).get(id_) if user: session.delete(user) session.commit() return True @strawberry.mutation def remove_profile(self, id_: int) -> bool: sync_engine = connect() with (Session(sync_engine) as session): profile = session.query(Profile).get(id_) if profile: session.delete(profile) session.commit() return True @strawberry.mutation def remove_schedule(self, id_: int) -> bool: sync_engine = connect() with (Session(sync_engine) as session): schedule = session.query(Schedule).get(id_) if schedule: session.delete(schedule) session.commit() return True @strawberry.mutation def send_commit(self, bndname: str, schema: str, commit_id: str) -> bool: sync_engine = connect() with (Session(sync_engine) as session): user = session.query(User).filter(User.bndname == bndname).one_or_none() if user: queue = Queue( user_id=user.id, commit_id=commit_id, schema=schema, ) session.add(queue) session.commit() return True def init(): sync_engine = connect() Base.metadata.drop_all(sync_engine) Base.metadata.create_all(sync_engine) with Session(sync_engine) as session: bnd127 = User( username="bnd127", passwd="gost_2012$a742ec53198ec2a5027086fba8814a89982a57112d1a72d02260161108f39b50", bndname="bnd127", newbnd=True, active=True, upstream=False ) bnd128 = User( username="bnd128", passwd="gost_2012$a742ec53198ec2a5027086fba8814a89982a57112d1a72d02260161108f39b50", bndname="bnd128", newbnd=True, active=True, upstream=False ) session.add_all([bnd127, bnd128]) session.commit() def main(): # init() uvicorn.run("main:app", port=9000, log_level="info", host='0.0.0.0') schema = strawberry.Schema(query=Query, mutation=Mutation) graphql_app = GraphQLRouter(schema, graphiql=True) app = FastAPI(root_path="/graphql") app.add_middleware(CORSMiddleware, allow_origins=['http://127.0.0.1:3000', 'http://localhost:3000', 'http://10.10.8.24:3000'], allow_credentials=True, allow_methods=['*'], allow_headers=['*'] ) app.include_router(graphql_app, prefix='/graphql') if __name__ == '__main__': main()