Add option to serve a static frontend
This commit is contained in:
parent
b3804f3d58
commit
f6117d04ee
9 changed files with 64 additions and 33 deletions
|
@ -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
|
||||
|
|
3
backend/.gitignore
vendored
3
backend/.gitignore
vendored
|
@ -1,3 +1,4 @@
|
|||
**/__pycache__
|
||||
simple-ocpp-cs.db
|
||||
.env
|
||||
.env
|
||||
static
|
|
@ -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()
|
||||
|
|
|
@ -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')
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@
|
|||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<a href="/chargepoint/{transaction.chargepoint.id}" class="btn btn-sm btn-primary">
|
||||
<a href="#/chargepoint/{transaction.chargepoint.id}" class="btn btn-sm btn-primary">
|
||||
<i class="bi bi-plug-fill text-lg"></i>
|
||||
{transaction.chargepoint.identity}
|
||||
</a>
|
||||
|
@ -71,7 +71,7 @@
|
|||
>{((transaction.meter_end - transaction.meter_start) * transaction.price).toFixed(2)} €</td
|
||||
>
|
||||
<th>
|
||||
<a href="/transaction/{transaction.id}" class="btn btn-sm btn-primary">
|
||||
<a href="#/transaction/{transaction.id}" class="btn btn-sm btn-primary">
|
||||
{$i18n.t('common:transactionTable.detailButton')}
|
||||
<i class="bi bi-arrow-right"></i>
|
||||
</a>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
let { children } = $props()
|
||||
|
||||
if (!$persistentSettings.loggedIn) {
|
||||
goto('/login')
|
||||
goto('#/login')
|
||||
}
|
||||
|
||||
let drawerOpen = $state(false)
|
||||
|
@ -44,7 +44,7 @@
|
|||
onclick={() => {
|
||||
drawerOpen = !drawerOpen
|
||||
}}
|
||||
href="/idtoken"
|
||||
href="#/idtoken"
|
||||
>
|
||||
<i class="bi bi-credit-card-fill text-xl"></i>
|
||||
<span>{$i18n.t('common:navbar.link.idtoken')}</span>
|
||||
|
@ -55,7 +55,7 @@
|
|||
onclick={() => {
|
||||
drawerOpen = !drawerOpen
|
||||
}}
|
||||
href="/transaction"
|
||||
href="#/transaction"
|
||||
>
|
||||
<i class="bi bi-battery-charging text-xl"></i>
|
||||
<span>{$i18n.t('common:navbar.link.transaction')}</span>
|
||||
|
@ -66,7 +66,7 @@
|
|||
onclick={() => {
|
||||
drawerOpen = !drawerOpen
|
||||
}}
|
||||
href="/chargepoint"
|
||||
href="#/chargepoint"
|
||||
>
|
||||
<i class="bi bi-plug-fill text-xl"></i>
|
||||
<span>{$i18n.t('common:navbar.link.chargepoint')}</span>
|
||||
|
@ -87,7 +87,7 @@
|
|||
tabindex="-1"
|
||||
class="menu menu-sm dropdown-content bg-base-100 rounded-box z-1 mt-3 w-52 p-2 shadow"
|
||||
>
|
||||
<li><a href="/profile">{$i18n.t('common:navbar.link.profile')}</a></li>
|
||||
<li><a href="#/profile">{$i18n.t('common:navbar.link.profile')}</a></li>
|
||||
<li><button onclick={logout}>{$i18n.t('common:navbar.link.logout')}</button></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
@ -81,7 +81,7 @@
|
|||
</td>
|
||||
<td>{chargepoint.price} €</td>
|
||||
<th>
|
||||
<a href="/chargepoint/{chargepoint.id}" class="btn btn-sm btn-primary">
|
||||
<a href="#/chargepoint/{chargepoint.id}" class="btn btn-sm btn-primary">
|
||||
{$i18n.t('common:transactionTable.detailButton')}
|
||||
<i class="bi bi-arrow-right"></i>
|
||||
</a>
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
export const ssr = false
|
|
@ -11,6 +11,7 @@ const config = {
|
|||
adapter: adapter({
|
||||
fallback: 'index.html',
|
||||
}),
|
||||
router: { type: 'hash' },
|
||||
},
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue