Skip to content

Backend and main repo for ShareABike. Connect lock adapter and admin ui/client app to have a complete bike-sharing system

Notifications You must be signed in to change notification settings

Retch/shareabike

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ShareABike main repository

Documentation Admin UI
Documentation Omni Adapter
Documentation Flutter App

Introduction

System in action

Lock.mp4

Service schema

Use the docker-compose.yml file from this repository to configure and run the software stack. service schema

Setup

System requirements

  • Linux server
  • Docker and Docker Compose
  • AMD64 or ARM64 CPU

Depending on the amount of users and locks interacting with the system, you should have enough ram and cpu power available. The following screenshot shows a test system with one lock and one admin user connected:

System memory usage

With Docker

To run the software stack with docker, the only files needed are docker-compose.yml and nginx.conf. Configure the values to your needs. The setup should work out of the box, but here is a list of values that are important to be changed.

Change values

  • APP_SECRET: Symfony documentation
  • ADAPTER_USERNAME: Username which is used by the adapter with basic auth to send data to the backend
  • ADAPTER_PASSWORDHASH: Generate a secure hash with this tool and your chosen adapter password
  • CORS_ALLOW_ORIGIN: The url where your ui is hosted, to allow all origins (dev purposes) put in "*"
  • BACKEND_USERNAME: Same as ADAPTER_USERNAME
  • BACKEND_PASSWORD: The Password corresponding to the ADAPTER_PASSWORDHASH

Useful commands

Start
docker compose up -d
Stop
docker compose down
Logs
docker compose logs -f
Update
docker compose pull

Public access

With reverse proxy (tested with caddy)

Put container and ports behind their own subdomains.

Admin UI

shareabike-admin:80

API

shareabike-backend-nginx:80

pgAdmin

shareabike-pgadmin:80

Add admin user

Via pgadmin

The docker-compose.yml file already contains a pgadmin service definition. Pgadmin can be used to manage the database through the web browser.

Add server in pgadmin

The db needs to be added once, change user and password to your choice.

View table data in pgadmin

After adding the server, the data can be edited like this. Insert an entry to table user with roles column like following and a hash (bcrypt.online).

Via database tool

You need to access the database to create an administrator account. To access the db, use the down command and add a port forwarding like this to the db part in the docker-compose.yml file:

Add port to compose

Now you can access the db directly or via ssh with your favorite database tool. Insert an entry to table user with roles column like following and a hash (bcrypt.online).

Add entry

Configure omni lock

Lock configuration is done via an app called BleTool. First insert the sim card. You have to modify the server domain/ip (omni adapter host) and APN for the sim card network to work. A test case code is needed to add a test case You can request the app apk and a test case from omni support.

Visualization

BleTool

BleTool

Login

Login

Test case

Add test case

Set omni adapter host

Set adapter

Api Documentation

For better understanding, all api routes come with sample data. Values in [] are placeholder.

Adapter

Update Status

The adapter sends data to the backend via the adapter endpoint. Only values that changed are sent from the adapter to the backend. All values are optional.

Request
curl -u [ADAPTER_BACKEND_USER]:[ADAPTER_BACKEND_PASSWORD] --location 'http://[BACKEND_HOST]/adapter/[IMEI]/updatestatus' \
--header 'Content-Type: application/json' \
--data '{
    "packetType": "S5Packet",
    "voltage": 4.02,
    "isLocked": false,
    "csq": 27,
    "satellites": 8,
    "noGps": false,
    "hdop": 1.5,
    "altitude": 259.2,
    "longitudeHemisphere": "N",
    "latitudeHemisphere": "E",
    "longitudeDegrees": 11.038,
    "latitudeDegrees": 50.963,
    "btMac": "D3:D3:62:AA:11:11",
    "lockSwVersion": "124",
    "lockHwRevision": "C9",
    "lockSwDate": "2022-06-20",
    "event": "recovery"
}'
Response

Status codes:

  • 200: Data was successfully received
  • 401: Probably username or password not correct
  • 404: Probably lock with given IMEI does not exist in backend database

Auth

The backend uses jwt auth with refresh token in cookie.

Login

Request
curl --location 'http://[BACKEND_HOST]/api/login_check' \
--header 'Content-Type: application/json' \
--data-raw '{
    "username": [USER_EMAIL],
    "password": [USER_PASSWORD]
}'
Response

If successful, a jwt will be returned in the body and the refresh token will be contained in a cookie called refresh_token.

Check jwt

To check the validity of a jwt. Jwt validity period can be adjusted in the compose.

Request
curl --location 'http://[BACKEND_HOST]/api/jwt_check' \
--header 'Authorization: Bearer [JWT]'
Response

Status codes:

  • 200: jwt is valid
  • 401: jwt is invalid

Refresh

Because of security reasons, the jwt has a very limited validity time period. For that reason, a new jwt has to be requested with passing the previously received refresh_token cookie.

Request
curl --location 'http://[BACKEND_HOST]/api/token/refresh' \
--header 'Cookie: refresh_token=[REFRESH_TOKEN_COOKIE]'
Response

If successful, a new jwt will be returned in the body.

Logout

The refresh_token cookie has to be sent along, so it can be invalidated and the user logged out. Remember to remove the jwt from the frontend application also.

Request
curl --location 'http://[BACKEND_HOST]/api/token/invalidate' \
--header 'Cookie: refresh_token=[REFRESH_TOKEN_COOKIE]'
Response

If successful, http 200 will be returned.

Admin

Get All Locks

Request
curl --location 'http://[BACKEND_HOST]/api/admin/locks' \
--header 'Authorization: Bearer [JWT]'
Response

Array of locks

{
    "locks": [
        {
            "id": 1,
            "deviceId": "123456789",
            "qrCodeContent": "123456",
            "isLocked": true,
            "lockTypeDescription": "omni",
            "isConnectedToAdapter": true,
            "lastEvent": null,
            "lastEventUtcTimestamp": null,
            "lastContactUtcTimestamp": 1693299451,
            "batteryPercentage": 76,
            "cellularSignalQualityPercentage": 51,
            "noGps": false,
            "lastPositionTimeUtcTimestamp": 1693228697,
            "satellites": 8,
            "hdop": 1,
            "latitudeDegrees": 50.964,
            "longitudeDegrees": 11.048,
            "latitudeHemisphere": "N",
            "longitudeHemisphere": "E"
        }
    ]
}

Get All Lock types

Request
curl --location 'http://[BACKEND_HOST]/api/admin/locktypes' \
--header 'Authorization: Bearer [JWT]'
Response

Array of lock types

{
    "lockTypes": [
        {
            "id": 1,
            "description": "omni",
            "batteryVoltageMin": 3.4,
            "batteryVoltageMax": 4.2,
            "cellularSignalQualityMin": 2,
            "cellularSignalQualityMax": 32
        }
    ]
}

Add lock

Request
curl --location 'http://[BACKEND_HOST]/api/admin/lock' \
--header 'Authorization: Bearer [JWT]' \
--header 'Content-Type: application/json' \
--data '{
    "deviceId": "123456789",
    "qrCodeContent": "123456",
    "lockTypeId": 1
}'
Response

Status codes:

  • 200: Lock added successfully
  • 409: Lock with lock id already exists

Add Lock Type

With a fresh setup and a clean database, you have to define the lock type. At the moment, only omni locks are supported, so the table will only have one lock type entry.
!Important: When adding a lock type "family" and the lock type you want to add is from brand omni lock or follows the protocol, be sure to use "omni" as description string because some lock specific functions as unlocking are specific to the lock type. The backend will check the lock type description and when it contains "omni" the omni specific functions are going to work.

Request
curl --location 'http://[BACKEND_HOST]/api/admin/locktype' \
--header 'Authorization: Bearer [JWT]' \
--header 'Content-Type: application/json' \
--data '{
    "description": "omni",
    "batteryVoltageMin": 3.4,
    "batteryVoltageMax": 4.2,
    "cellularSignalQualityMin": 2,
    "cellularSignalQualityMax": 32
}'
Response

Status codes:

  • 200: Lock type added successfully
  • 409: Lock type with description already exists

Update Lock Type

Request
curl --location --request PUT 'http://[BACKEND_HOST]/api/admin/locktype' \
--header 'Authorization: Bearer [JWT]' \
--header 'Content-Type: application/json' \
--data '{
    "id": [LOCKID],
    "description": "omni update",
    "batteryVoltageMin": 3.4,
    "batteryVoltageMax": 4.2,
    "cellularSignalQualityMin": 2,
    "cellularSignalQualityMax": 32
}'
Response

Status codes:

  • 200: Lock type updated successfully
  • 409: Lock type with description already exists

Delete Lock Type

Request
curl --location --request DELETE 'http://[BACKEND_HOST]/api/admin/locktype/[LOCKID]' \
--header 'Authorization: Bearer [JWT]'
Response

Status codes:

  • 200: Lock type deleted successfully
  • 409: Lock type with given id does not exist

Unlock lock

Request
curl --location 'http://[BACKEND_HOST]/api/admin/requestunlock/[LOCKID]' \
--header 'Authorization: Bearer [JWT]'
Response

Status codes:

  • 200: Lock should unlock now
  • 500: Probably lock id not exist
  • 501: Adapter type unlock not implemented in backend

Trigger lock beep

Request
curl --location 'http://[BACKEND_HOST]/api/admin/requestring/[LOCKID]' \
--header 'Authorization: Bearer [JWT]'
Response

Status codes:

  • 200: Lock should beep now
  • 500: Probably lock id not exist
  • 501: Adapter type ring not implemented in backend

Ask lock gps position information

Request
curl --location 'http://[BACKEND_HOST]/api/admin/requestposition/[LOCKID]' \
--header 'Authorization: Bearer [JWT]'
Response

Status codes:

  • 200: Lock should fetch gps position and send it back a few minutes later
  • 500: Probably lock id not exist
  • 501: Adapter type ask position not implemented in backend

Ask lock general information

Request
curl --location 'http://[BACKEND_HOST]/api/admin/requestinfo/[LOCKID]' \
--header 'Authorization: Bearer [JWT]'
Response

Status codes:

  • 200: Lock should fetch general information and send it back shortly
  • 500: Probably lock id not exist
  • 501: Adapter type ask information not implemented in backend

User

To be implemented

About

Backend and main repo for ShareABike. Connect lock adapter and admin ui/client app to have a complete bike-sharing system

Topics

Resources

Stars

Watchers

Forks

Packages