Skip to content

API backend for QChat in QGIS and other GIS clients

License

Notifications You must be signed in to change notification settings

geotribu/gischat

Repository files navigation

GISChat - Backend API

Code style: black flake8 Imports: isort pre-commit.ci status

🎳 Tester 🐍 Linter ⚒️ Deploy docker image codecov


Gischat API backend for chatting with your tribe in GIS tools like QGIS (using QTribu plugin available from official plugin repository), GIS mobile apps and other to come

No database : messages are not stored. Just stateless websockets.

Known instances

Following instances are up and running :

URL Description Location
https://gischat.geotribu.net "official" international instance Germany
https://gischat.geotribu.fr "official" french-speaking instance Germany

Developer information

  • Rooms can be fetched using the /rooms endpoint
  • Rules can be fetched using the /rules endpoint
  • Number of connected users can be fetched using the /status endpoint
  • List of connected and registered users can be fetched using the /room/{room}/users endpoint
  • New users must connect a websocket to the /room/{room_name}/ws endpoint
  • After connecting to the websocket, it is possible to register the user in the room by sending a newcomer message (see below)
  • Messages passing through the websocket are strings with a JSON structure, they have a type key which represent which kind of message it is

JSON message types

Those are the messages that might transit through the websocket.

Each of them has a "type" key based on which it is possible to parse them :

  1. "text": basic text message send by someone in the room, e.g.:

    {
      "type": "text",
      "author": "jane_doe",
      "avatar": "mGeoPackage.svg",
      "text": "Hi @all how are you doing ?"
    }

    "author" value must be alphanumeric (or _ or -) and have min / max length set by MIN_AUTHOR_LENGTH / MAX_AUTHOR_LENGTH environment variables
    avatar value is optional and usually points to a QGIS resource icon (see the ones available in the QChat/QTribu plugin)
    "text" value must have max length set by MAX_MESSAGE_LENGTH environment variable

  2. "image": image message send by someone in the room, e.g.:

    {
      "type": "image",
      "author": "jane_doe",
      "avatar": "mIconPostgis.svg",
      "image_data": "utf-8 string of the image encoded in base64"
    }

    The image will be resized by the backend before broadcast, using the MAX_IMAGE_SIZE environment variable value

  3. "nb_users": notifies about the number of users connected to the room, e.g.:

    {
      "type": "nb_users",
      "nb_users": 36
    }
  4. "newcomer": someone has just registered in the room, e.g.:

    {
      "type": "newcomer",
      "newcomer": "jane_doe"
    }

    After having connected to the websocket, it is possible to register a user by sending a newcomer message

  5. "exiter": someone registered has left the room, e.g.:

    {
      "type": "exiter",
      "exiter": "jane_doe"
    }
  6. "like": someone has liked a message, e.g.:

    {
      "type": "like",
      "liker_author": "john_doe",
      "liked_author": "jane_doe",
      "message": "Hi @john_doe how are you doing ?"
    }

    means that john_doe liked jane_doe's message ("Hi @john_doe how are you doing ?")

    The messages of the like type are sent only to the liked author, if this user is registered. If this user is not registered, it won't be notified

  7. "geojson": someone shared a geojson layer, e.g.:

    {
       "type": "geojson",
       "author": "jane_doe",
       "avatar": "mIconPostgis.svg",
       "layer_name": "my_geojson_layer",
       "crs_wkt": 'GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]]',
       "crs_authid": "EPSG:4326",
       "geojson": {
          "type": "FeatureCollection",
          "features": [
             {
                "type": "Feature",
                "properties": {
                   "attribute_1": "something"
                },
                "geometry": {
                   "type": "Point",
                   "coordinates": [
                      1,
                      2
                   ]
                }
             },
             ...
          ]
       }
    }

    The coordinates of the geojson features must be expressed using the provided crs_wkt and crs_authid

  8. "crs": someone shared a CRS, e.g.:

    {
       "type": "geojson",
       "author": "jane_doe",
       "avatar": "mIconPostgis.svg",
       "crs_wkt": 'GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]]',
       "crs_authid": "EPSG:4326",
    }
    
  9. "bbox": someone shared a bbox, e.g.:

    {
       "type": "geojson",
       "author": "jane_doe",
       "avatar": "mIconPostgis.svg",
       "crs_wkt": 'GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]]',
       "crs_authid": "EPSG:4326",
       "xmin": -1.1,
       "xmax": 1.1,
       "ymin": -1.1,
       "ymax": 1.1
    }
  10. "uncompliant": someone sent an uncompliant message and the server answers with such a message, e.g.:

    {
       "type": "uncompliant",
       "reason": "Too many geojson features : 600 vs max 500 allowed"
    }

    This example is the server response if sending a ŋeojson message with too many features

Deploy a self-hosted instance

Setup Gischat backend

Note

ROOMS environment variable is a comma-separated list of strings which represent the available chat rooms.
RULES environment variable describes the instance's rules. Useful information that users should know, even when skimming content.
MAX_IMAGE_SIZE environment variable describes the max size of image in pixels. The server will resize images based on this value.
MAX_GEOJSON_FEATURES environment variable describes the max number of features allowed in a geojson message. If there is more feature, the message will not be considered and the server will respond with a uncompliant message.

  1. Install docker using the official documentation

  2. Create a docker-compose.yaml file on your server:

    services:
      api:
        image: gounux/gischat:latest
        container_name: gischat-app
        environment:
          - ROOMS=QGIS,Field and mobile,GIS tribe, Living room,Kitchen,Garden
          - RULES=Be kind and nice to this wonderful world
          - MAIN_LANGUAGE=en
          - ENVIRONMENT=production
          - MIN_AUTHOR_LENGTH=3
          - MAX_AUTHOR_LENGTH=32
          - MAX_MESSAGE_LENGTH=255
          - MAX_IMAGE_SIZE=800
          - MAX_GEOJSON_FEATURES=500
        ports:
          - 8000:8000
        restart: unless-stopped
  3. Launch the app using compose:

    docker compose up -d
  4. Have a look at the logs:

    docker compose logs -f

Run behind a nginx proxy using HTTPS

  1. Install nginx and certbot:

    apt install nginx certbot
  2. Get a Let's Encrypt SSL certificate for your domain:

    # stop nginx service
    systemctl stop nginx
    # get the certificate
    certbot certonly --standalone -d DOMAIN
    # restart nginx service
    systemctl start nginx

    The Let's Encrypt SSL certificate should be saved to /etc/letsencrypt/live/DOMAIN

  3. Create a nginx config file:

    touch /etc/nginx/sites-available/gischat.conf
  4. Edit the file and add the following content (using your domain):

    upstream gischat_upstream {
      server 127.0.0.1:8000;
    }
    
    server {
      listen 80;
      server_name <DOMAIN>;
      return 301 https://$host$request_uri;
    }
    
    server {
    
      listen 443 ssl;
      server_name <DOMAIN>;
    
      ssl_certificate /etc/letsencrypt/live/<DOMAIN>/fullchain.pem;
      ssl_certificate_key /etc/letsencrypt/live/<DOMAIN>/privkey.pem;
    
      location / {
        proxy_pass http://gischat_upstream;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_read_timeout 86400;
      }
    }
  5. Create a symlink to enable the nginx config file :

    ln -s /etc/nginx/sites-available/gischat.conf /etc/nginx/sites-enabled/gischat.conf
  6. Check that the nginx config file is okey :

    nginx -t
    nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
    nginx: configuration file /etc/nginx/nginx.conf test is successful
  7. Reload nginx configuration

    systemctl reload nginx

That's it, you should be able to chat now with your fellow GIS mates !

Development

  1. Install poetry:
python -m pip install poetry
  1. Install project's dependencies using poetry:
poetry install
  1. Install pre-commit tools:
poetry run pre-commit install
  1. Create local environment file and edit it:
cp .env.example .env
  1. Launch API:
poetry run uvicorn gischat.app:app --reload --env-file .env --log-config=log_config.yaml

Build

  1. Build docker image:
docker build . --tag geotribu/gischat:latest
  1. Run docker image:
docker run geotribu/gischat:latest --env ROOMS=QGIS,QField,Geotribu --env RULES="Those are the rules: ..."