diff --git a/README.md b/README.md index e8dbadc..6a3d965 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# Simple OCCP Central System +# LibreCharge -This is a simple implementation of a basic [OCPP](https://openchargealliance.org/protocols/open-charge-point-protocol/) 2.0.1 compliant central system (backend) for EV charging stations. +LibreCharge is a simple implementation of a basic [OCPP](https://openchargealliance.org/protocols/open-charge-point-protocol/) 2.0.1 compliant central system (backend) for EV charging stations. ## Features @@ -12,5 +12,7 @@ This is a simple implementation of a basic [OCPP](https://openchargealliance.org - ✅ RESTful API ## Tested charging stations + This project has been successfully tested with the following charging stations: + - Alfen Eve Single S-line diff --git a/backend/.gitignore b/backend/.gitignore index 097cd22..f305e97 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -1,3 +1,4 @@ **/__pycache__ simple-ocpp-cs.db -.env \ No newline at end of file +.env +static \ No newline at end of file diff --git a/backend/app/main.py b/backend/app/main.py index 5a69345..c3bf377 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -1,6 +1,8 @@ +import os from dotenv import load_dotenv from fastapi import APIRouter, FastAPI from fastapi.middleware.cors import CORSMiddleware +from fastapi.staticfiles import StaticFiles from starlette.middleware.authentication import AuthenticationMiddleware load_dotenv() @@ -17,25 +19,46 @@ from app.routers import ( ) from app.security.websocket_auth_backend import BasicAuthBackend -def create_ocpp_app(): - app_ocpp = FastAPI( - responses={404: {"description": "Not found"}}, - ) - app_ocpp.include_router(ocpp_v1.router) - app_ocpp.add_middleware(AuthenticationMiddleware, backend=BasicAuthBackend()) - - return app_ocpp - def create_app(): - app = FastAPI( - title="simple-ocpp-cs", - version="0.2.0", - summary="Simple implementation of a basic OCPP 2.0.1 compliant central system (backend) for EV charging stations", - responses={404: {"description": "Not found"}}, + # Common app config + title="LibreCharge" + version="0.2.0" + summary="Simple implementation of a basic OCPP 2.0.1 compliant central system (backend) for EV charging stations" + responses={404: {"description": "Not found"}} + + # Root FastAPI app + root_app = FastAPI( + title=title, + version=version, + summary=summary, + responses=responses, + docs_url=None, + redoc_url=None, + ) + + # FastAPI app for OCPP handler + ocpp_app = FastAPI( + title=title, + version=version, + summary=summary, + responses=responses, + docs_url=None, + redoc_url=None, + ) + ocpp_app.include_router(ocpp_v1.router) + ocpp_app.add_middleware(AuthenticationMiddleware, backend=BasicAuthBackend()) + root_app.mount(path="/v1/ocpp", app=ocpp_app) + + # FastAPI app for REST API + api_app = FastAPI( + title=title, + version=version, + summary=summary, + responses=responses, ) api_v1_router = APIRouter( - prefix="/api/v1" + prefix="/v1" ) api_v1_router.include_router(auth_v1.router) api_v1_router.include_router(chargepoint_v1.router) @@ -45,15 +68,20 @@ def create_app(): api_v1_router.include_router(meter_value_v1.router) api_v1_router.include_router(transaction_v1.router) - app.include_router(api_v1_router) - app.mount(path="/v1/ocpp", app=create_ocpp_app()) + api_app.include_router(api_v1_router) + root_app.mount(path="/api", app=api_app) + + # Serve static files if existent + if os.path.isdir('static'): + static_files = StaticFiles(directory="static", html=True) + root_app.mount(path="/", app=static_files) origins = [ "http://localhost", "http://localhost:5173", ] - app.add_middleware( + root_app.add_middleware( CORSMiddleware, allow_origins=origins, allow_credentials=True, @@ -61,6 +89,6 @@ def create_app(): allow_headers=["*"], ) - return app + return root_app app = create_app() diff --git a/frontend/src/lib/axios.svelte.ts b/frontend/src/lib/axios.svelte.ts index d086718..f176748 100644 --- a/frontend/src/lib/axios.svelte.ts +++ b/frontend/src/lib/axios.svelte.ts @@ -55,7 +55,7 @@ function createTokenRefreshInterceptor() { // Retry failed, clean up and reject the promise clearLoginState(); axios.defaults.headers.common['Authorization'] = ""; - goto('/login?reauth') + goto('#/login?reauth') return Promise.reject(retryError); }) .finally(createTokenRefreshInterceptor); // Re-attach interceptor for future requests @@ -101,7 +101,7 @@ export const logout = function() { .then(() => { clearLoginState(); axios.defaults.headers.common['Authorization'] = ""; - goto('/login?logout') + goto('#/login?logout') }); } diff --git a/frontend/src/lib/component/TransactionTable.svelte b/frontend/src/lib/component/TransactionTable.svelte index 6b5a73e..d9c9e86 100644 --- a/frontend/src/lib/component/TransactionTable.svelte +++ b/frontend/src/lib/component/TransactionTable.svelte @@ -59,7 +59,7 @@