Compare commits

..

No commits in common. "7780f247fb6519976baa8d9840b59d1f15cb3395" and "e33d74308a412ca4ccdd77d9a23fff72209b33c6" have entirely different histories.

9 changed files with 27 additions and 160 deletions

View file

@ -1,13 +1,11 @@
from fastapi import FastAPI, Request from fastapi import FastAPI
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
from starlette.middleware.authentication import AuthenticationMiddleware from starlette.middleware.authentication import AuthenticationMiddleware
import uvicorn import uvicorn
from app.database import engine, Base from app.database import engine, Base
from app.models import * from app.models import *
from app.routers import chargepoint_v1, id_token_v1, ocpp_v1, user_v1 from app.routers import chargepoint_v1, user_v1, ocpp_v1
from app.util.websocket_auth_backend import BasicAuthBackend from app.util.websocket_auth_backend import BasicAuthBackend
Base.metadata.create_all(bind=engine) Base.metadata.create_all(bind=engine)
@ -27,7 +25,6 @@ def create_app():
) )
app.include_router(chargepoint_v1.router, prefix="/v1") app.include_router(chargepoint_v1.router, prefix="/v1")
app.include_router(id_token_v1.router, prefix="/v1")
app.include_router(user_v1.router, prefix="/v1") app.include_router(user_v1.router, prefix="/v1")
app.mount(path="/v1/ocpp", app=create_ocpp_app()) app.mount(path="/v1/ocpp", app=create_ocpp_app())

View file

@ -11,11 +11,6 @@ class ChargePoint(Base):
friendly_name = Column(String, unique=True, index=True) friendly_name = Column(String, unique=True, index=True)
is_active = Column(Boolean, default=True) is_active = Column(Boolean, default=True)
password = Column(String) password = Column(String)
last_seen = Column(DateTime, nullable=True) last_seen = Column(DateTime, nullable=True)
vendor_name = Column(String, nullable=True)
model = Column(String, nullable=True)
serial_number = Column(String, nullable=True)
firmware_version = Column(String, nullable=True)
connectors = relationship("Connector", cascade="delete, delete-orphan") connectors = relationship("Connector", cascade="delete, delete-orphan")

View file

@ -8,7 +8,7 @@ class IdToken(Base):
__tablename__ = "id_tokens" __tablename__ = "id_tokens"
id = Column(Uuid, primary_key=True, default=uuid.uuid4) id = Column(Uuid, primary_key=True, default=uuid.uuid4)
friendly_name = Column(String) title = Column(String)
is_active = Column(Boolean, default=True) is_active = Column(Boolean, default=True)
token = Column(String, index=True) token = Column(String, index=True)

View file

@ -4,11 +4,10 @@ import os
from ocpp.routing import on from ocpp.routing import on
from ocpp.v201 import ChargePoint as cp from ocpp.v201 import ChargePoint as cp
from ocpp.v201 import call_result from ocpp.v201 import call_result
from ocpp.v201.datatypes import IdTokenInfoType, IdTokenType from ocpp.v201.enums import Action, RegistrationStatusType, AuthorizationStatusType
from ocpp.v201.enums import Action, RegistrationStatusType, AuthorizationStatusType, IdTokenType as IdTokenEnumType
from app.database import SessionLocal from app.database import SessionLocal
from app.models.chargepoint import ChargePoint as DbChargePoint from app.models.chargepoint import ChargePoint
from app.models.connector import Connector from app.models.connector import Connector
from app.models.id_token import IdToken from app.models.id_token import IdToken
from app.schemas.connector import ConnectorStatus from app.schemas.connector import ConnectorStatus
@ -16,13 +15,10 @@ from app.schemas.connector import ConnectorStatus
class ChargePoint(cp): class ChargePoint(cp):
@on(Action.BootNotification) @on(Action.BootNotification)
async def on_boot_notification(self, charging_station, **kwargs): async def on_boot_notification(self, charging_station, reason, **kwargs):
with SessionLocal() as db: with SessionLocal() as db:
db_chargepoint = db.query(DbChargePoint).filter(DbChargePoint.friendly_name == self.id).first() db_chargepoint = db.query(ChargePoint).filter(ChargePoint.friendly_name == self.id).first()
db_chargepoint.last_seen = datetime.now(UTC) db_chargepoint.last_seen = datetime.now(UTC)
for key in charging_station.keys():
if key in db_chargepoint.__dict__:
setattr(db_chargepoint, key, charging_station[key])
db.commit() db.commit()
return call_result.BootNotificationPayload( return call_result.BootNotificationPayload(
current_time=datetime.now(UTC).isoformat(), current_time=datetime.now(UTC).isoformat(),
@ -33,7 +29,7 @@ class ChargePoint(cp):
@on(Action.Heartbeat) @on(Action.Heartbeat)
async def on_heartbeat_request(self): async def on_heartbeat_request(self):
with SessionLocal() as db: with SessionLocal() as db:
db_chargepoint = db.query(DbChargePoint).filter(DbChargePoint.friendly_name == self.id).first() db_chargepoint = db.query(ChargePoint).filter(ChargePoint.friendly_name == self.id).first()
db_chargepoint.last_seen = datetime.now(UTC) db_chargepoint.last_seen = datetime.now(UTC)
db.commit() db.commit()
return call_result.HeartbeatPayload( return call_result.HeartbeatPayload(
@ -43,7 +39,7 @@ class ChargePoint(cp):
@on(Action.StatusNotification) @on(Action.StatusNotification)
async def on_status_notification(self, evse_id: int, connector_id: int, connector_status: str, **kwargs): async def on_status_notification(self, evse_id: int, connector_id: int, connector_status: str, **kwargs):
with SessionLocal() as db: with SessionLocal() as db:
db_chargepoint = db.query(DbChargePoint).filter(DbChargePoint.friendly_name == self.id).first() db_chargepoint = db.query(ChargePoint).filter(ChargePoint.friendly_name == self.id).first()
db_chargepoint.last_seen = datetime.now(UTC) db_chargepoint.last_seen = datetime.now(UTC)
db_connector = db.query(Connector).filter( db_connector = db.query(Connector).filter(
@ -68,38 +64,24 @@ class ChargePoint(cp):
@on(Action.Authorize) @on(Action.Authorize)
async def on_authorize(self, id_token, **kwargs): async def on_authorize(self, id_token, **kwargs):
if id_token["type"] not in ["ISO14443", "ISO15693"]: if id_token == None:
return call_result.AuthorizePayload( return call_result.AuthorizePayload(id_token_info={'status': AuthorizationStatusType.invalid})
id_token_info=IdTokenInfoType( if id_token.type != "ISO14443" | "ISO15693":
status=AuthorizationStatusType.invalid return call_result.AuthorizePayload(id_token_info={'status': AuthorizationStatusType.invalid})
)
)
with SessionLocal() as db: with SessionLocal() as db:
db_chargepoint = db.query(DbChargePoint).filter(DbChargePoint.friendly_name == self.id).first() db_chargepoint = db.query(ChargePoint).filter(ChargePoint.friendly_name == self.id).first()
db_chargepoint.last_seen = datetime.now(UTC) db_chargepoint.last_seen = datetime.now(UTC)
db_id_token = db.query(IdToken).filter(IdToken.token == id_token["id_token"]).first() db_id_token = db.query(IdToken).filter(IdToken.token == id_token.id).first()
db.commit() db.commit()
if db_id_token == None: if db_id_token == None:
id_token_info=IdTokenInfoType( return call_result.AuthorizePayload(id_token_info={'status': AuthorizationStatusType.unknown})
status=AuthorizationStatusType.unknown
)
else:
if db_id_token.is_active == False: if db_id_token.is_active == False:
id_token_info=IdTokenInfoType( return call_result.AuthorizePayload(id_token_info={'status': AuthorizationStatusType.blocked})
status=AuthorizationStatusType.blocked
) return call_result.AuthorizePayload(id_token_info={'status': AuthorizationStatusType.accepted, 'groupIdToken': str(db_id_token.owner_id)})
else:
id_token_info=IdTokenInfoType(
status=AuthorizationStatusType.accepted,
group_id_token=IdTokenType(
type=IdTokenEnumType.central,
id_token=str(db_id_token.owner_id)
)
)
return call_result.AuthorizePayload(id_token_info)
@on(Action.TransactionEvent) @on(Action.TransactionEvent)
async def on_transaction_event(self): async def on_transaction_event(self):

View file

@ -12,8 +12,8 @@ from app.models.chargepoint import ChargePoint as DbChargePoint
from app.security import get_api_key from app.security import get_api_key
router = APIRouter( router = APIRouter(
prefix="/chargepoints", prefix="/chargepoint",
tags=["Chargepoint (v1)"], tags=["chargepoint (v1)"],
) )
@router.get(path="", response_model=list[ChargePoint]) @router.get(path="", response_model=list[ChargePoint])

View file

@ -1,96 +0,0 @@
from uuid import UUID
from fastapi import APIRouter, HTTPException, Security
from fastapi.exceptions import RequestValidationError
from fastapi.params import Depends
from sqlalchemy.orm import Session
from app.database import get_db
from app.schemas.id_token import IdToken, IdTokenCreate, IdTokenUpdate
from app.models.id_token import IdToken as DbIdToken
from app.models.user import User as DbUser
from app.security import get_api_key
router = APIRouter(
prefix="/id-tokens",
tags=["IdToken (v1)"]
)
@router.get(path="", response_model=list[IdToken])
async def get_it_tokens(
skip: int = 0,
limit: int = 20,
api_key: str = Security(get_api_key),
db: Session = Depends(get_db)
):
return db.query(DbIdToken).offset(skip).limit(limit).all()
@router.get(path="/{id_token_id}", response_model=IdToken)
async def get_id_token(
id_token_id: UUID,
api_key: str = Security(get_api_key),
db: Session = Depends(get_db)
):
id_token = db.get(DbIdToken, id_token_id)
if id_token == None:
raise HTTPException(status_code=404, detail="IdToken not found")
return id_token
@router.post(path="", status_code=201, response_model=IdToken)
async def create_id_token(
create_id_token: IdTokenCreate,
api_key: str = Security(get_api_key),
db: Session = Depends(get_db)
):
owner = db.get(DbUser, create_id_token.owner_id)
if owner == None:
raise HTTPException(status_code=422, detail=[{
"loc": ["body", "owner_id"],
"msg": "Owner not found",
"type": "invalid_relation"
}])
id_token = DbIdToken(
friendly_name=create_id_token.friendly_name,
is_active=create_id_token.is_active,
token=create_id_token.token,
owner_id=create_id_token.owner_id
)
db.add(id_token)
db.commit()
db.refresh(id_token)
return id_token
@router.patch(path="/{id_token_id}", response_model=IdToken)
async def update_id_token(
id_token_id: UUID,
id_token_update: IdTokenUpdate,
api_key: str = Security(get_api_key),
db: Session = Depends(get_db)
):
id_token = db.get(DbIdToken, id_token_id)
if id_token is None:
raise HTTPException(status_code=404, detail="IdToken not found")
for key, value in id_token_update.model_dump(exclude_unset=True).items():
if key == "owner_id":
owner = db.get(DbUser, value)
if owner == None:
raise HTTPException(status_code=422, detail=[{
"loc": ["body", "owner_id"],
"msg": "Owner not found",
"type": "invalid_relation"
}])
setattr(id_token, key, value)
db.commit()
return id_token
@router.delete(path="/{id_token_id}", response_model=None)
async def delete_id_token(
id_token_id: UUID,
api_key: str = Security(get_api_key),
db: Session = Depends(get_db)
):
id_token = db.get(DbIdToken, id_token_id)
if id_token == None:
raise HTTPException(status_code=404, detail="IdToken not found")
db.delete(id_token)
db.commit()
return []

View file

@ -9,8 +9,8 @@ from app.models.user import User as DbUser
from app.security import get_api_key from app.security import get_api_key
router = APIRouter( router = APIRouter(
prefix="/users", prefix="/user",
tags=["User (v1)"], tags=["user (v1)"],
) )
@router.get(path="", response_model=list[User]) @router.get(path="", response_model=list[User])

View file

@ -19,10 +19,6 @@ class ChargePointCreate(ChargePointBase):
class ChargePoint(ChargePointBase): class ChargePoint(ChargePointBase):
id: UUID id: UUID
last_seen: datetime | None last_seen: datetime | None
vendor_name: str | None
model: str | None
serial_number: str | None
firmware_version: str | None
connectors: list[Connector] = [] connectors: list[Connector] = []
class Config: class Config:

View file

@ -1,25 +1,18 @@
from typing import Optional
from uuid import UUID from uuid import UUID
from pydantic import BaseModel from pydantic import BaseModel
from app.schemas.user import User from app.schemas.user import User
class IdTokenBase(BaseModel): class IdTokenBase(BaseModel):
friendly_name: str title: str
is_active: bool is_active: bool
owner_id: UUID
token: str
class IdTokenCreate(IdTokenBase): class IdTokenCreate(IdTokenBase):
pass pass
class IdTokenUpdate(BaseModel):
friendly_name: Optional[str] = None
is_active: Optional[bool] = None
owner_id: Optional[UUID] = None
class IdToken(IdTokenBase): class IdToken(IdTokenBase):
id: UUID id: UUID
owner: User
class Config: class Config:
from_attributes = True from_attributes = True