Connected service management front to back; migrated to formik
This commit is contained in:
@ -1,17 +1,55 @@
|
||||
from sqlalchemy import Column
|
||||
from sqlalchemy import Column, ForeignKey
|
||||
from sqlalchemy import Integer
|
||||
from sqlalchemy import VARCHAR
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from api.database import Base
|
||||
from api.database import engine
|
||||
|
||||
|
||||
class UserModel(Base):
|
||||
class User(Base):
|
||||
__tablename__ = "user"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True, nullable=False)
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
name = Column(VARCHAR(length=32), nullable=False, unique=True)
|
||||
password_hash = Column(VARCHAR, nullable=False)
|
||||
|
||||
|
||||
class Service(Base):
|
||||
__tablename__ = "service"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
name = Column(VARCHAR, nullable=False, unique=True)
|
||||
|
||||
# children
|
||||
loadbalancer = relationship(
|
||||
"Loadbalancer", cascade="all,delete", uselist=False, backref="service"
|
||||
)
|
||||
|
||||
|
||||
class Loadbalancer(Base):
|
||||
__tablename__ = "loadbalancer"
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
|
||||
# parent
|
||||
service_id = Column(Integer, ForeignKey("service.id"))
|
||||
|
||||
# children
|
||||
servers = relationship(
|
||||
"LoadbalancerServers",
|
||||
cascade="all,delete",
|
||||
backref="loadbalancer",
|
||||
lazy="dynamic",
|
||||
)
|
||||
|
||||
|
||||
class LoadbalancerServers(Base):
|
||||
__tablename__ = "loadbalancerservers"
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
address = Column(VARCHAR, nullable=False)
|
||||
|
||||
# parent
|
||||
loadbalancer_id = Column(Integer, ForeignKey("loadbalancer.id"))
|
||||
|
||||
|
||||
Base.metadata.create_all(engine)
|
||||
|
@ -1,24 +1,32 @@
|
||||
import strawberry
|
||||
|
||||
from api.schema.definitions.auth import AuthResult
|
||||
from api.schema.definitions.auth import login
|
||||
from api.schema.definitions.auth import update_me
|
||||
from api.schema.definitions.common import CommonMessage
|
||||
import api.schema.definitions.auth as auth
|
||||
import api.schema.permissions as permissions
|
||||
import api.schema.definitions.service as service
|
||||
from api.schema.extensions import extensions
|
||||
from api.schema.permissions import IsAuthenticated
|
||||
|
||||
user_perms = {
|
||||
"permission_classes": [
|
||||
# permissions.IsAuthenticated
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@strawberry.type
|
||||
class Query:
|
||||
hello: str
|
||||
# service
|
||||
services = strawberry.field(resolver=service.get_services, **user_perms)
|
||||
|
||||
|
||||
@strawberry.type
|
||||
class Mutation:
|
||||
login: AuthResult = strawberry.field(resolver=login)
|
||||
update_me: CommonMessage = strawberry.field(
|
||||
resolver=update_me, permission_classes=[IsAuthenticated]
|
||||
)
|
||||
# auth
|
||||
login = strawberry.field(resolver=auth.login)
|
||||
update_me = strawberry.field(resolver=auth.update_me, **user_perms)
|
||||
|
||||
# service
|
||||
add_service = strawberry.field(resolver=service.add_service, **user_perms)
|
||||
remove_service = strawberry.field(resolver=service.remove_service, **user_perms)
|
||||
|
||||
|
||||
schema = strawberry.Schema(query=Query, mutation=Mutation, extensions=extensions)
|
||||
|
@ -2,11 +2,10 @@ from typing import TYPE_CHECKING
|
||||
|
||||
import strawberry
|
||||
from fastapi import Request
|
||||
from sqlalchemy import true
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from api.hasher import argon2_hasher
|
||||
from api.models import UserModel
|
||||
from api.models import User as UserModel
|
||||
from api.schema.definitions.common import CommonError
|
||||
from api.schema.definitions.common import CommonMessage
|
||||
from api.schema.definitions.user import User
|
||||
|
105
api/schema/definitions/service.py
Normal file
105
api/schema/definitions/service.py
Normal file
@ -0,0 +1,105 @@
|
||||
import strawberry
|
||||
import typing
|
||||
import api.models as models
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from strawberry.types import Info
|
||||
from api.schema import Query
|
||||
|
||||
|
||||
@strawberry.input
|
||||
class AddLoadbalancerInput:
|
||||
servers: typing.List[str]
|
||||
|
||||
|
||||
@strawberry.input
|
||||
class AddServiceInput:
|
||||
name: str
|
||||
loadbalancer: AddLoadbalancerInput
|
||||
|
||||
|
||||
@strawberry.input
|
||||
class ServiceFilterInput:
|
||||
id: typing.Optional[int] = None
|
||||
search: typing.Optional[str] = None
|
||||
|
||||
|
||||
@strawberry.type
|
||||
class LoadbalancerServer:
|
||||
id: int
|
||||
address: str
|
||||
|
||||
@classmethod
|
||||
def from_instance(cls, instance: models.LoadbalancerServers):
|
||||
return cls(id=instance.id, address=instance.address)
|
||||
|
||||
|
||||
@strawberry.type
|
||||
class Loadbalancer:
|
||||
servers: typing.List[LoadbalancerServer]
|
||||
|
||||
@classmethod
|
||||
def from_instance(cls, instance: models.Loadbalancer):
|
||||
if not instance:
|
||||
return cls(servers=[])
|
||||
|
||||
return cls(
|
||||
servers=[
|
||||
LoadbalancerServer.from_instance(server) for server in instance.servers
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
@strawberry.type
|
||||
class Service:
|
||||
id: int
|
||||
name: str
|
||||
loadbalancer: Loadbalancer
|
||||
|
||||
@classmethod
|
||||
def from_instance(cls, instance: models.Service):
|
||||
return cls(
|
||||
id=instance.id,
|
||||
name=instance.name,
|
||||
loadbalancer=Loadbalancer.from_instance(instance.loadbalancer),
|
||||
)
|
||||
|
||||
|
||||
async def get_services(
|
||||
root: "Query", info: "Info", body: typing.Optional[ServiceFilterInput] = None
|
||||
) -> typing.List[Service]:
|
||||
db: Session = info.context["db"]
|
||||
stmt = db.query(models.Service)
|
||||
|
||||
if body:
|
||||
if body.id:
|
||||
stmt = stmt.filter(models.Service.id == body.id)
|
||||
elif body.search:
|
||||
stmt = stmt.filter(models.Service.name.like("%" + body.search + "%"))
|
||||
|
||||
services = [service[0] for service in db.execute(stmt).all()]
|
||||
return [Service.from_instance(service) for service in services]
|
||||
|
||||
|
||||
async def remove_service(root: "Query", info: "Info", id: int) -> None:
|
||||
db: Session = info.context["db"]
|
||||
db.query(models.Service).filter(models.Service.id == id).delete()
|
||||
db.commit()
|
||||
return None
|
||||
|
||||
|
||||
async def add_service(root: "Query", info: "Info", body: AddServiceInput) -> Service:
|
||||
db: Session = info.context["db"]
|
||||
service = models.Service(name=body.name)
|
||||
|
||||
if body.loadbalancer:
|
||||
lb = models.Loadbalancer()
|
||||
for server in body.loadbalancer.servers:
|
||||
lb.servers.append(models.LoadbalancerServers(address=server))
|
||||
|
||||
service.loadbalancer = lb
|
||||
|
||||
db.add(service)
|
||||
db.commit()
|
||||
return Service.from_instance(service)
|
@ -1,6 +1,6 @@
|
||||
import strawberry
|
||||
|
||||
from api.models import UserModel
|
||||
import api.models as models
|
||||
|
||||
|
||||
@strawberry.type
|
||||
@ -9,5 +9,5 @@ class User:
|
||||
name: str
|
||||
|
||||
@classmethod
|
||||
def from_instance(cls, instance: UserModel):
|
||||
def from_instance(cls, instance: models.User):
|
||||
return cls(id=instance.id, name=instance.name)
|
||||
|
21
api/seed.py
21
api/seed.py
@ -2,13 +2,28 @@ from sqlalchemy.orm import Session
|
||||
|
||||
from api.database import SessionLocal
|
||||
from api.hasher import argon2_hasher
|
||||
from api.models import UserModel
|
||||
import api.models as models
|
||||
|
||||
|
||||
def seed():
|
||||
db: Session = SessionLocal()
|
||||
|
||||
if db.query(UserModel).count() == 0:
|
||||
admin = UserModel(name="admin", password_hash=argon2_hasher.hash("admin"))
|
||||
if db.query(models.User).count() == 0:
|
||||
admin = models.User(name="admin", password_hash=argon2_hasher.hash("admin"))
|
||||
db.add(admin)
|
||||
db.commit()
|
||||
|
||||
if db.query(models.Service).count() == 0:
|
||||
lb = models.Loadbalancer()
|
||||
lb.servers.append(
|
||||
models.LoadbalancerServers(address="http://192.168.2.134:8000/")
|
||||
)
|
||||
lb.servers.append(
|
||||
models.LoadbalancerServers(address="http://192.168.2.132:80/")
|
||||
)
|
||||
|
||||
test_service = models.Service(name="service")
|
||||
test_service.loadbalancer = lb
|
||||
|
||||
db.add(test_service)
|
||||
db.commit()
|
||||
|
@ -5,10 +5,9 @@ from datetime import timedelta
|
||||
from datetime import timezone
|
||||
|
||||
import jwt
|
||||
from fastapi import Request
|
||||
from starlette.datastructures import Headers
|
||||
|
||||
from api.models import UserModel
|
||||
import api.models as models
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
UserTokenData = dict[str, typing.Any]
|
||||
@ -17,7 +16,7 @@ JWT_SECRET = os.getenv("JWT_SECRET", "")
|
||||
|
||||
|
||||
def encode_user_token(
|
||||
user: UserModel, expire_in: timedelta = timedelta(hours=6)
|
||||
user: models.User, expire_in: timedelta = timedelta(hours=6)
|
||||
) -> str:
|
||||
payload = {}
|
||||
payload["id"] = user.id
|
||||
|
Reference in New Issue
Block a user