Compare commits
12 commits
0.5.0-alph
...
master
Author | SHA1 | Date | |
---|---|---|---|
8d34c450e3 | |||
a3a2efe471 | |||
d54bb178dd | |||
93b9903e0d | |||
e53ed92a56 | |||
c4dba88ed6 | |||
6035f93868 | |||
cf98b3b160 | |||
151bceb8c5 | |||
3a84d920c1 | |||
12d57f0de9 | |||
bd7b4724d6 |
19 changed files with 553 additions and 110 deletions
|
@ -1,5 +1,4 @@
|
|||
.venv
|
||||
.git
|
||||
dist
|
||||
.pytest_cache
|
||||
.mypy_cache
|
||||
|
|
|
@ -30,6 +30,8 @@ jobs:
|
|||
echo '${{ toJSON(env) }}'
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install app with 'uv pip install'
|
||||
run: uv pip install --python=$UV_PROJECT_ENVIRONMENT --no-deps .
|
||||
|
@ -48,40 +50,35 @@ jobs:
|
|||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Get the version from git
|
||||
id: version
|
||||
run: echo "version=$(git describe --dirty --tags)" >> $GITHUB_OUTPUT
|
||||
- name: Install the latest version of uv
|
||||
uses: astral-sh/setup-uv@v4
|
||||
with:
|
||||
version: "0.6.6"
|
||||
|
||||
- name: Check if the container should be built
|
||||
id: builder
|
||||
env:
|
||||
RUN: ${{ toJSON(inputs.build || !contains(steps.version.outputs.version, '-g')) }}
|
||||
run: |
|
||||
echo "run=$RUN" >> $GITHUB_OUTPUT
|
||||
echo "Run build: $RUN"
|
||||
- name: Install
|
||||
run: uv sync
|
||||
|
||||
- name: Info - version and test if the git version is clean (then python package and image container should be built)
|
||||
env:
|
||||
VERSION: ${{ steps.version.outputs.version }}
|
||||
RUN: ${{ steps.builder.outputs.run }}
|
||||
FORCE: ${{ toJSON(inputs.build) }}
|
||||
run: |
|
||||
echo "Version $VERSION, force (manual input): $FORCE, run the build: $RUN"
|
||||
|
||||
- name: Set the version in pyproject.toml (workaround for uv not supporting dynamic version)
|
||||
if: fromJSON(steps.builder.outputs.run)
|
||||
env:
|
||||
VERSION: ${{ steps.version.outputs.version }}
|
||||
run: sed "s/0.0.0/${VERSION}/" -i pyproject.toml
|
||||
- name: Get version
|
||||
run: echo "VERSION=$(.venv/bin/dunamai from any --style semver)" >> $GITHUB_ENV
|
||||
|
||||
- name: Version
|
||||
run: echo $VERSION
|
||||
|
||||
- name: Get distance from tag
|
||||
run: echo "DISTANCE=$(.venv/bin/dunamai from any --format '{distance}')" >> $GITHUB_ENV
|
||||
|
||||
- name: Distance
|
||||
run: echo $DISTANCE
|
||||
|
||||
- name: Workaround for bug of podman-login
|
||||
if: fromJSON(steps.builder.outputs.run)
|
||||
if: env.DISTANCE == '0'
|
||||
run: |
|
||||
mkdir -p $HOME/.docker
|
||||
echo "{ \"auths\": {} }" > $HOME/.docker/config.json
|
||||
|
||||
- name: Log in to the container registry (with another workaround)
|
||||
if: fromJSON(steps.builder.outputs.run)
|
||||
if: env.DISTANCE == '0'
|
||||
uses: actions/podman-login@v1
|
||||
with:
|
||||
registry: ${{ vars.REGISTRY }}
|
||||
|
@ -90,37 +87,31 @@ jobs:
|
|||
auth_file_path: /tmp/auth.json
|
||||
|
||||
- name: Build the container image
|
||||
if: fromJSON(steps.builder.outputs.run)
|
||||
if: env.DISTANCE == '0'
|
||||
uses: actions/buildah-build@v1
|
||||
with:
|
||||
image: gisaf-backend
|
||||
oci: true
|
||||
labels: gisaf-backend
|
||||
tags: latest ${{ steps.version.outputs.version }}
|
||||
tags: "latest ${{ env.VERSION }}"
|
||||
containerfiles: |
|
||||
./Containerfile
|
||||
build-args: |
|
||||
APP_VERSION=${{ steps.version.outputs.version }}
|
||||
|
||||
- name: Push the image to the registry
|
||||
if: fromJSON(steps.builder.outputs.run)
|
||||
if: env.DISTANCE == '0'
|
||||
uses: actions/push-to-registry@v2
|
||||
with:
|
||||
registry: "docker://${{ vars.REGISTRY }}/${{ vars.ORGANISATION }}"
|
||||
image: gisaf-backend
|
||||
tags: latest ${{ steps.version.outputs.version }}
|
||||
tags: "latest ${{ env.VERSION }}"
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v4
|
||||
with:
|
||||
version: "0.5.9"
|
||||
|
||||
- name: Build python package
|
||||
if: fromJSON(steps.builder.outputs.run)
|
||||
- name: Build wheel
|
||||
if: env.DISTANCE == '0'
|
||||
run: uv build --wheel
|
||||
|
||||
- name: Publish Python package (home)
|
||||
if: fromJSON(steps.builder.outputs.run)
|
||||
if: env.DISTANCE == '0'
|
||||
env:
|
||||
LOCAL_PYPI_TOKEN: ${{ secrets.LOCAL_PYPI_TOKEN }}
|
||||
run: uv publish --publish-url https://code.philo.ydns.eu/api/packages/philorg/pypi --token $LOCAL_PYPI_TOKEN
|
||||
continue-on-error: true
|
||||
|
|
|
@ -1,19 +1,17 @@
|
|||
# Build: podman build -t code.philo.ydns.eu/philorg/gisaf-backend -f Containerfile
|
||||
|
||||
FROM code.philo.ydns.eu/philorg/gisaf-backend-deps
|
||||
FROM docker.io/library/python:latest
|
||||
|
||||
ENV PYTHONPATH $UV_PROJECT_ENVIRONMENT/lib/python3.12/site-packages
|
||||
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /usr/local/bin/
|
||||
ENV GISAF__BACKEND__PORT 8898
|
||||
ENV GISAF__BACKEND__BIND__ADDR 0.0.0.0
|
||||
ARG APP_VERSION=0.0.0
|
||||
|
||||
COPY . /src
|
||||
COPY . /app
|
||||
|
||||
RUN uv pip install \
|
||||
--python=$UV_PROJECT_ENVIRONMENT \
|
||||
--no-deps \
|
||||
/src
|
||||
# Sync the project into a new environment, using the frozen lockfile
|
||||
WORKDIR /app
|
||||
|
||||
RUN uv pip install --system .
|
||||
|
||||
RUN echo $APP_VERSION > /app/version.txt
|
||||
|
||||
CMD uvicorn gisaf.application:app --port ${GISAF__BACKEND__PORT} --host ${GISAF__BACKEND__BIND__ADDR}
|
||||
|
|
30
README.md
30
README.md
|
@ -1,23 +1,40 @@
|
|||
# Gisaf
|
||||
|
||||
*Gisaf* is a web based GIS (Geographical Information System) initially developed
|
||||
for the CSR Goematics unit in Auroville, India.
|
||||
for the CSR Geomatics unit in Auroville, India.
|
||||
|
||||
Its main audiences are local administrations which need a platform
|
||||
to develop and maintain a geographical, topological and environmental data,
|
||||
allow teams of surveyors to upload data and update the GIS in near real time,
|
||||
and collect data from a wide range of sources.
|
||||
|
||||
Although it just works out of the box, it is primarily intended to be customized
|
||||
and extended with the data, uses, needs.
|
||||
|
||||
## Features
|
||||
|
||||
- Layers defined with Python plugins or ANSI-standard categories
|
||||
- Open source industry standard interfaces (PostGIS database, OGCAPI)
|
||||
- Support of different geographical projections
|
||||
- Integrated administration interface
|
||||
- Export of data from standard formats (Geopackage, Shapefile)
|
||||
- Import and update of data managed in a well defined workflow with "baskets"
|
||||
- Acquisition of different kinds of surveyors' equipments, accuracies
|
||||
- Robust mechanisms of identification of the data sources, survey times
|
||||
- Geographical feature statuses (eg. Existing, Deprecated, Future)
|
||||
- Role-based access to data
|
||||
- Support temporal and other data for different features
|
||||
- Easy integration with IoT sensors, eg. using the MQTT protocol
|
||||
- Detailed information for all features
|
||||
- Tagging of features
|
||||
- Customizable, user triggered actions
|
||||
- Background map
|
||||
- Customizable background maps
|
||||
- Customizable dashboards
|
||||
- Customizable base maps (domain specific set of layers)
|
||||
- Python and Jupyter integration: easy to interact with, add features, components,
|
||||
explore data and conduct scientific research
|
||||
- Plugin architecture: add functions with Python packages
|
||||
- Free and open source
|
||||
|
||||
## Software stack
|
||||
|
||||
|
@ -31,3 +48,12 @@ for the CSR Goematics unit in Auroville, India.
|
|||
## Installation / deployment
|
||||
|
||||
See the documentation in the deployment directory.
|
||||
|
||||
## Configuration
|
||||
|
||||
Documentation is being rewritten. Please contact us.
|
||||
|
||||
## Demo
|
||||
|
||||
A very basic demo without any data, basically to give a feel of the user interface,
|
||||
is available [here](https://gisaf.philo.ydns.eu).
|
||||
|
|
105
deployment/README.md
Normal file
105
deployment/README.md
Normal file
|
@ -0,0 +1,105 @@
|
|||
# Gisaf deployment / installation
|
||||
|
||||
This documentation covers the specifics of Gisaf deployment, but
|
||||
it does no cover application configuration details,
|
||||
the set up of a public facing web server handling dns domain / https security.
|
||||
|
||||
*Gisaf* is shipped as containers for easy and effective installation,
|
||||
and packages to deploy them for different environments.
|
||||
|
||||
There are 4 containers:
|
||||
|
||||
- Frontend (Angular build: static files, served by an nginx process,
|
||||
eventually forwarding requests to the backend)
|
||||
- Backend (Python server)
|
||||
- Postgis database server
|
||||
- Redis server
|
||||
|
||||
Commands below assume that they are run from their respective directories
|
||||
(`deployment/systemd`, `deployment/kubernetes`, ...).
|
||||
|
||||
## Systemd
|
||||
|
||||
With the help of *podman*, systemd can handle starting/restarting services, etc,
|
||||
even with a non-privileged user.
|
||||
|
||||
The 4 containers are in the same *pod*, as it is meant primarily for a lightweight
|
||||
production environment, but this can be easily adapted.
|
||||
|
||||
The `deployment/systemd` directory contains files used for running *Gisaf* with Systemd.
|
||||
Podman is used to facilitate the creation of the relevant services.
|
||||
|
||||
### Deployment
|
||||
|
||||
```sh
|
||||
cp -r * $HOME/.config/containers/systemd/
|
||||
systemctl --user daemon-reload
|
||||
systemctl --user start gisaf-pod.service
|
||||
```
|
||||
|
||||
Environment variables for the Gisaf configuration can be added
|
||||
in the `gisaf-backend.container` file.
|
||||
|
||||
Note that starting on system boot requires the user to be enabled accordingly with:
|
||||
|
||||
```sh
|
||||
sudo loginctl enable-linger <username>
|
||||
```
|
||||
|
||||
## Plain Kubernetes (no Helm)
|
||||
|
||||
The `kubernetes` directory contains files for deployment on Kubernetes.
|
||||
|
||||
The standard installation uses a namespace named `gisaf`.
|
||||
|
||||
The Kubernetes configuration is split in 2 files: `gisaf.yaml` and `config.yaml`
|
||||
|
||||
Deployment:
|
||||
|
||||
```sh
|
||||
kubectl create namespace gisaf
|
||||
kubectl apply -f config.yaml
|
||||
kubectl apply -f gisaf.yaml
|
||||
```
|
||||
|
||||
Update after modification on the server (frontend and backend):
|
||||
|
||||
```sh
|
||||
kubectl --namespace gisaf rollout restart deployment gisaf-server-deployment
|
||||
```
|
||||
|
||||
## Helm
|
||||
|
||||
The `helm` chart is in the directory named `helm`.
|
||||
|
||||
### Deploy on Kubernetes
|
||||
|
||||
Deploying with Helm on Kubernetes makes it straightforward to
|
||||
run on cloud services.
|
||||
|
||||
```sh
|
||||
kubectl create namespace gisaf
|
||||
helm install gisaf helm
|
||||
```
|
||||
|
||||
#### Update
|
||||
|
||||
```sh
|
||||
helm upgrade gisaf helm
|
||||
```
|
||||
|
||||
### Publish the Helm chart
|
||||
|
||||
First, build the Helm package:
|
||||
|
||||
```sh
|
||||
helm package helm
|
||||
```
|
||||
|
||||
Then upload it:
|
||||
|
||||
```sh
|
||||
helm --user phil:<password> -X POST \
|
||||
--upload-file gisaf-0.1.0.tgz \
|
||||
https://code.philo.ydns.eu/api/packages/philorg/helm/api/charts
|
||||
```
|
1
deployment/kubernetes/.gitignore
vendored
Normal file
1
deployment/kubernetes/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
gisaf-*.tgz
|
10
deployment/kubernetes/config.yaml
Normal file
10
deployment/kubernetes/config.yaml
Normal file
|
@ -0,0 +1,10 @@
|
|||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: gisaf-config
|
||||
namespace: gisaf
|
||||
data:
|
||||
GISAF__MAP__LNG: "6.178"
|
||||
GISAF__MAP__LAT: "45.8818"
|
||||
GISAF__MAP__ZOOM: "14"
|
||||
GISAF__MAP__PITCH: "40"
|
|
@ -2,6 +2,7 @@ apiVersion: v1
|
|||
kind: Service
|
||||
metadata:
|
||||
name: gisaf-database
|
||||
namespace: gisaf
|
||||
labels:
|
||||
app: gisaf-database
|
||||
spec:
|
||||
|
@ -18,6 +19,7 @@ apiVersion: apps/v1
|
|||
kind: Deployment
|
||||
metadata:
|
||||
name: gisaf-database-deployment
|
||||
namespace: gisaf
|
||||
labels:
|
||||
app: gisaf-database
|
||||
spec:
|
||||
|
@ -27,6 +29,7 @@ spec:
|
|||
app: gisaf-database
|
||||
template:
|
||||
metadata:
|
||||
namespace: gisaf
|
||||
labels:
|
||||
app: gisaf-database
|
||||
spec:
|
||||
|
@ -71,6 +74,7 @@ apiVersion: v1
|
|||
kind: Service
|
||||
metadata:
|
||||
name: gisaf-server
|
||||
namespace: gisaf
|
||||
labels:
|
||||
app: gisaf-server
|
||||
spec:
|
||||
|
@ -87,6 +91,7 @@ apiVersion: apps/v1
|
|||
kind: Deployment
|
||||
metadata:
|
||||
name: gisaf-server-deployment
|
||||
namespace: gisaf
|
||||
labels:
|
||||
app: gisaf-server
|
||||
spec:
|
||||
|
@ -96,6 +101,7 @@ spec:
|
|||
app: gisaf-server
|
||||
template:
|
||||
metadata:
|
||||
namespace: gisaf
|
||||
labels:
|
||||
app: gisaf-server
|
||||
spec:
|
||||
|
@ -106,7 +112,7 @@ spec:
|
|||
- "gisaf-backend"
|
||||
initContainers:
|
||||
- name: gisaf-backend-initdb
|
||||
image: code.philo.ydns.eu/philorg/gisaf-backend:0.5.0-alpha.8
|
||||
image: code.philo.ydns.eu/philorg/gisaf-backend:0.5.0-alpha.10
|
||||
imagePullPolicy: Always
|
||||
command: ["gisaf", "create-db"]
|
||||
env:
|
||||
|
@ -114,13 +120,16 @@ spec:
|
|||
value: gisaf-database
|
||||
containers:
|
||||
- name: gisaf-backend
|
||||
image: code.philo.ydns.eu/philorg/gisaf-backend:0.5.0-alpha.8
|
||||
image: code.philo.ydns.eu/philorg/gisaf-backend:0.5.0-alpha.10
|
||||
imagePullPolicy: Always
|
||||
env:
|
||||
- name: GISAF__GISAF_LIVE__REDIS
|
||||
value: redis://gisaf-database
|
||||
- name: GISAF__DB__HOST
|
||||
value: gisaf-database
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: gisaf-config
|
||||
- name: gisaf-frontend
|
||||
image: code.philo.ydns.eu/philorg/gisaf-frontend:0.5.0-alpha.13
|
||||
imagePullPolicy: Always
|
||||
|
@ -137,11 +146,12 @@ apiVersion: v1
|
|||
kind: PersistentVolume
|
||||
metadata:
|
||||
name: gisaf-pgdata-pv
|
||||
namespace: gisaf
|
||||
labels:
|
||||
type: local
|
||||
app: gisaf-postgres
|
||||
spec:
|
||||
storageClassName: manual
|
||||
storageClassName: local-path
|
||||
capacity:
|
||||
storage: 2Gi
|
||||
accessModes:
|
||||
|
@ -154,11 +164,12 @@ apiVersion: v1
|
|||
kind: PersistentVolume
|
||||
metadata:
|
||||
name: gisaf-redis-pv
|
||||
namespace: gisaf
|
||||
labels:
|
||||
type: local
|
||||
app: gisaf-redis
|
||||
spec:
|
||||
storageClassName: manual
|
||||
storageClassName: local-path
|
||||
capacity:
|
||||
storage: 2Gi
|
||||
accessModes:
|
||||
|
@ -171,8 +182,9 @@ apiVersion: v1
|
|||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: gisaf-pgdata-pvc
|
||||
namespace: gisaf
|
||||
spec:
|
||||
storageClassName: manual
|
||||
storageClassName: local-path
|
||||
accessModes:
|
||||
- ReadWriteMany
|
||||
resources:
|
||||
|
@ -184,8 +196,9 @@ apiVersion: v1
|
|||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: gisaf-redis-pvc
|
||||
namespace: gisaf
|
||||
spec:
|
||||
storageClassName: manual
|
||||
storageClassName: local-path
|
||||
accessModes:
|
||||
- ReadWriteMany
|
||||
resources:
|
||||
|
@ -197,7 +210,7 @@ apiVersion: networking.k8s.io/v1
|
|||
kind: Ingress
|
||||
metadata:
|
||||
name: gisaf
|
||||
namespace: default
|
||||
namespace: gisaf
|
||||
#annotations:
|
||||
# kubernetes.io/ingress.class: traefik
|
||||
# #traefik.ingress.kubernetes.io/router.middlewares: default-strip-prefix@kubernetescrd
|
||||
|
|
23
deployment/kubernetes/helm/.helmignore
Normal file
23
deployment/kubernetes/helm/.helmignore
Normal file
|
@ -0,0 +1,23 @@
|
|||
# Patterns to ignore when building packages.
|
||||
# This supports shell glob matching, relative path matching, and
|
||||
# negation (prefixed with !). Only one pattern per line.
|
||||
.DS_Store
|
||||
# Common VCS dirs
|
||||
.git/
|
||||
.gitignore
|
||||
.bzr/
|
||||
.bzrignore
|
||||
.hg/
|
||||
.hgignore
|
||||
.svn/
|
||||
# Common backup files
|
||||
*.swp
|
||||
*.bak
|
||||
*.tmp
|
||||
*.orig
|
||||
*~
|
||||
# Various IDEs
|
||||
.project
|
||||
.idea/
|
||||
*.tmproj
|
||||
.vscode/
|
6
deployment/kubernetes/helm/Chart.yaml
Normal file
6
deployment/kubernetes/helm/Chart.yaml
Normal file
|
@ -0,0 +1,6 @@
|
|||
apiVersion: v2
|
||||
name: gisaf
|
||||
description: Gisaf Helm chart for Kubernetes
|
||||
type: application
|
||||
version: 0.1.0
|
||||
appVersion: "0.15.0-alpha"
|
10
deployment/kubernetes/helm/templates/config.yaml
Normal file
10
deployment/kubernetes/helm/templates/config.yaml
Normal file
|
@ -0,0 +1,10 @@
|
|||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-config
|
||||
namespace: gisaf
|
||||
data:
|
||||
GISAF__MAP__LNG: "{{ .Values.map.lng }}"
|
||||
GISAF__MAP__LAT: "{{ .Values.map.lat }}"
|
||||
GISAF__MAP__ZOOM: "{{ .Values.map.zoom }}"
|
||||
GISAF__MAP__PITCH: "{{ .Values.map.pitch }}"
|
135
deployment/kubernetes/helm/templates/database.yaml
Normal file
135
deployment/kubernetes/helm/templates/database.yaml
Normal file
|
@ -0,0 +1,135 @@
|
|||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: gisaf-database
|
||||
namespace: gisaf
|
||||
labels:
|
||||
app: gisaf-database
|
||||
spec:
|
||||
ports:
|
||||
- name: psql
|
||||
port: 5432
|
||||
- name: redis
|
||||
port: 6379
|
||||
selector:
|
||||
app: gisaf-database
|
||||
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: gisaf-database-deployment
|
||||
namespace: gisaf
|
||||
labels:
|
||||
app: gisaf-database
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: gisaf-database
|
||||
template:
|
||||
metadata:
|
||||
namespace: gisaf
|
||||
labels:
|
||||
app: gisaf-database
|
||||
spec:
|
||||
hostAliases:
|
||||
- ip: "127.0.0.1"
|
||||
hostnames:
|
||||
- "gisaf-redis"
|
||||
- "gisaf-database"
|
||||
containers:
|
||||
- name: gisaf-database
|
||||
image: code.philo.ydns.eu/philorg/gisaf-database:latest
|
||||
imagePullPolicy: Always
|
||||
args:
|
||||
- postgres
|
||||
volumeMounts:
|
||||
- mountPath: /var/lib/postgresql/data
|
||||
name: gisaf-pgdata
|
||||
ports:
|
||||
- containerPort: 5432
|
||||
name: psql
|
||||
- image: docker.io/library/redis:alpine
|
||||
imagePullPolicy: Always
|
||||
name: gisaf-redis
|
||||
args:
|
||||
- redis-server
|
||||
volumeMounts:
|
||||
- mountPath: /data
|
||||
name: gisaf-redis
|
||||
ports:
|
||||
- containerPort: 6379
|
||||
name: redis
|
||||
volumes:
|
||||
- name: gisaf-pgdata
|
||||
persistentVolumeClaim:
|
||||
claimName: gisaf-pgdata-pvc
|
||||
- name: gisaf-redis
|
||||
persistentVolumeClaim:
|
||||
claimName: gisaf-redis-pvc
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: gisaf-pgdata-pvc
|
||||
namespace: gisaf
|
||||
spec:
|
||||
storageClassName: local-path
|
||||
accessModes:
|
||||
- ReadWriteMany
|
||||
resources:
|
||||
requests:
|
||||
storage: 2Gi
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: gisaf-redis-pvc
|
||||
namespace: gisaf
|
||||
spec:
|
||||
storageClassName: local-path
|
||||
accessModes:
|
||||
- ReadWriteMany
|
||||
resources:
|
||||
requests:
|
||||
storage: 200Mi
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata:
|
||||
name: gisaf-pgdata-pv
|
||||
namespace: gisaf
|
||||
labels:
|
||||
type: local
|
||||
app: gisaf-postgres
|
||||
spec:
|
||||
storageClassName: local-path
|
||||
capacity:
|
||||
storage: 2Gi
|
||||
accessModes:
|
||||
- ReadWriteMany
|
||||
hostPath:
|
||||
path: /data/gisaf/postgresql
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata:
|
||||
name: gisaf-redis-pv
|
||||
namespace: gisaf
|
||||
labels:
|
||||
type: local
|
||||
app: gisaf-redis
|
||||
spec:
|
||||
storageClassName: local-path
|
||||
capacity:
|
||||
storage: 2Gi
|
||||
accessModes:
|
||||
- ReadWriteMany
|
||||
hostPath:
|
||||
path: /data/gisaf/redis
|
28
deployment/kubernetes/helm/templates/network.yaml
Normal file
28
deployment/kubernetes/helm/templates/network.yaml
Normal file
|
@ -0,0 +1,28 @@
|
|||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: gisaf
|
||||
namespace: gisaf
|
||||
#annotations:
|
||||
# kubernetes.io/ingress.class: traefik
|
||||
# #traefik.ingress.kubernetes.io/router.middlewares: default-strip-prefix@kubernetescrd
|
||||
spec:
|
||||
rules:
|
||||
- http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: gisaf-server
|
||||
port:
|
||||
number: 80
|
||||
- path: /api
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: gisaf-server
|
||||
port:
|
||||
number: 8081
|
||||
|
69
deployment/kubernetes/helm/templates/server.yaml
Normal file
69
deployment/kubernetes/helm/templates/server.yaml
Normal file
|
@ -0,0 +1,69 @@
|
|||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: gisaf-server
|
||||
namespace: gisaf
|
||||
labels:
|
||||
app: gisaf-server
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
selector:
|
||||
app: gisaf-server
|
||||
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: gisaf-server-deployment
|
||||
namespace: gisaf
|
||||
labels:
|
||||
app: gisaf-server
|
||||
spec:
|
||||
replicas: {{ .Values.replicaCount }}
|
||||
selector:
|
||||
matchLabels:
|
||||
app: gisaf-server
|
||||
template:
|
||||
metadata:
|
||||
namespace: gisaf
|
||||
labels:
|
||||
app: gisaf-server
|
||||
spec:
|
||||
hostAliases:
|
||||
- ip: "127.0.0.1"
|
||||
hostnames:
|
||||
- "gisaf-frontend"
|
||||
- "gisaf-backend"
|
||||
initContainers:
|
||||
- name: gisaf-backend-initdb
|
||||
image: "code.philo.ydns.eu/philorg/gisaf-backend:{{ .Values.version.backend }}"
|
||||
imagePullPolicy: Always
|
||||
command: ["gisaf", "create-db"]
|
||||
env:
|
||||
- name: GISAF__DB__HOST
|
||||
value: gisaf-database
|
||||
containers:
|
||||
- name: gisaf-backend
|
||||
image: "code.philo.ydns.eu/philorg/gisaf-backend:{{ .Values.version.backend }}"
|
||||
imagePullPolicy: Always
|
||||
env:
|
||||
- name: GISAF__GISAF_LIVE__REDIS
|
||||
value: redis://gisaf-database
|
||||
- name: GISAF__DB__HOST
|
||||
value: gisaf-database
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: gisaf-config
|
||||
- name: gisaf-frontend
|
||||
image: "code.philo.ydns.eu/philorg/gisaf-frontend:{{ .Values.version.frontend }}"
|
||||
imagePullPolicy: Always
|
||||
args:
|
||||
- nginx
|
||||
- -g
|
||||
- daemon off;
|
||||
ports:
|
||||
- containerPort: 80
|
||||
|
13
deployment/kubernetes/helm/values.yaml
Normal file
13
deployment/kubernetes/helm/values.yaml
Normal file
|
@ -0,0 +1,13 @@
|
|||
# Replicaset count for the server (2 pods: frontend and backend)
|
||||
replicaCount: 2
|
||||
|
||||
version:
|
||||
backend: 0.5.0-alpha.10
|
||||
frontend: 0.5.0-alpha.13
|
||||
|
||||
# Map config
|
||||
map:
|
||||
lng: "6.178"
|
||||
lat: "45.8818"
|
||||
zoom: "14"
|
||||
pitch: "40"
|
|
@ -1,7 +1,7 @@
|
|||
[project]
|
||||
name = "gisaf-backend"
|
||||
version = "0.0.0"
|
||||
description = "Gisaf backend"
|
||||
dynamic = ["version"]
|
||||
authors = [{ name = "phil", email = "phil.dev@philome.mooo.com" }]
|
||||
dependencies = [
|
||||
"aiopath>=0.7.1",
|
||||
|
@ -40,20 +40,9 @@ readme = "README.md"
|
|||
[project.scripts]
|
||||
gisaf = "gisaf.cli:cli"
|
||||
|
||||
[project.optional-dependencies]
|
||||
contextily = ["contextily>=1.4.0"]
|
||||
mqtt = ["aiomqtt>=1.2.1"]
|
||||
all = ["gisaf-backend[contextily]", "gisaf-backend[mqtt]"]
|
||||
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[tool.hatch.build.targets.wheel]
|
||||
packages = ["src/gisaf"]
|
||||
|
||||
[tool.uv]
|
||||
dev-dependencies = [
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"dunamai>=1.23.0",
|
||||
"ipdb>=0.13.13",
|
||||
"pandas-stubs>=2.1.4.231218",
|
||||
"pretty-errors>=1.2.25",
|
||||
|
@ -64,3 +53,27 @@ dev-dependencies = [
|
|||
"types-passlib>=1.7.7.20240311",
|
||||
"pytest>=8.3.4",
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
contextily = ["contextily>=1.4.0"]
|
||||
mqtt = ["aiomqtt>=1.2.1"]
|
||||
all = ["gisaf-backend[contextily]", "gisaf-backend[mqtt]"]
|
||||
|
||||
[build-system]
|
||||
requires = ["hatchling", "uv-dynamic-versioning"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[tool.hatch.version]
|
||||
source = "uv-dynamic-versioning"
|
||||
|
||||
[tool.hatch.build.targets.wheel]
|
||||
packages = ["src/gisaf"]
|
||||
|
||||
[tool.uv-dynamic-versioning]
|
||||
style = "semver"
|
||||
|
||||
[tool.uv]
|
||||
package = true
|
||||
|
||||
[tool.black]
|
||||
line-length = 98
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
import importlib.metadata
|
||||
|
||||
try:
|
||||
from dunamai import Version, Style
|
||||
|
||||
__version__ = Version.from_git().serialize(style=Style.SemVer, dirty=True)
|
||||
except ImportError:
|
||||
# __name__ could be used if the package name is the same
|
||||
# as the directory - not the case here
|
||||
# __version__ = importlib.metadata.version(__name__)
|
||||
__version__ = importlib.metadata.version("gisaf-backend")
|
|
@ -14,25 +14,7 @@ from pydantic_settings import (
|
|||
YamlConfigSettingsSource,
|
||||
)
|
||||
|
||||
|
||||
def get_version():
|
||||
version = importlib_version("gisaf-backend")
|
||||
if version == "0.0.0":
|
||||
from subprocess import run
|
||||
|
||||
try:
|
||||
v_git_describe = run(
|
||||
["git", "describe", "--always", "--tags", "--dirty"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
cwd=Path(__file__).parent.parent,
|
||||
)
|
||||
v_git_describe.check_returncode()
|
||||
version = v_git_describe.stdout.strip()
|
||||
except Exception:
|
||||
logger.warn("Version not set in Python package and cannot run git describe")
|
||||
version = "?"
|
||||
return version
|
||||
from gisaf import __version__
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
@ -48,12 +30,8 @@ config_files = [
|
|||
|
||||
class DashboardHome(BaseModel):
|
||||
title: str = "Gisaf - home/dashboards"
|
||||
content_file: Path = (
|
||||
Path(Path.cwd().root) / "etc" / "gisaf" / "dashboard_home_content.html"
|
||||
)
|
||||
footer_file: Path = (
|
||||
Path(Path.cwd().root) / "etc" / "gisaf" / "dashboard_home_footer.html"
|
||||
)
|
||||
content_file: Path = Path(Path.cwd().root) / "etc" / "gisaf" / "dashboard_home_content.html"
|
||||
footer_file: Path = Path(Path.cwd().root) / "etc" / "gisaf" / "dashboard_home_footer.html"
|
||||
|
||||
|
||||
class GisafConfig(BaseModel):
|
||||
|
@ -141,7 +119,9 @@ class DB(BaseModel):
|
|||
echo: bool = False
|
||||
|
||||
def get_sqla_url(self):
|
||||
return f"postgresql+asyncpg://{self.user}:{self.password}@{self.host}:{self.port}/{self.db}"
|
||||
return (
|
||||
f"postgresql+asyncpg://{self.user}:{self.password}@{self.host}:{self.port}/{self.db}"
|
||||
)
|
||||
|
||||
def get_pg_url(self):
|
||||
return f"postgresql://{self.user}:{self.password}@{self.host}:{self.port}/{self.db}"
|
||||
|
@ -223,9 +203,7 @@ class OGCAPI(BaseModel):
|
|||
class TileServer(BaseModel):
|
||||
baseDir: Path = Path(BaseDirectory.xdg_data_home) / "gisaf" / "mbtiles_files_dir"
|
||||
useRequestUrl: bool = False
|
||||
spriteBaseDir: Path = (
|
||||
Path(BaseDirectory.xdg_data_home) / "gisaf" / "mbtiles_sprites_dir"
|
||||
)
|
||||
spriteBaseDir: Path = Path(BaseDirectory.xdg_data_home) / "gisaf" / "mbtiles_sprites_dir"
|
||||
spriteUrl: str = "/tiles/sprite/sprite"
|
||||
spriteBaseUrl: str = "https://gisaf.example.org"
|
||||
openMapTilesKey: str | None = None
|
||||
|
@ -332,9 +310,7 @@ class Config(BaseSettings):
|
|||
dotenv_settings: PydanticBaseSettingsSource,
|
||||
file_secret_settings: PydanticBaseSettingsSource,
|
||||
) -> Tuple[PydanticBaseSettingsSource, ...]:
|
||||
configs = [
|
||||
YamlConfigSettingsSource(settings_cls, yaml_file=cf) for cf in config_files
|
||||
]
|
||||
configs = [YamlConfigSettingsSource(settings_cls, yaml_file=cf) for cf in config_files]
|
||||
return (
|
||||
env_settings,
|
||||
init_settings,
|
||||
|
@ -370,7 +346,7 @@ class Config(BaseSettings):
|
|||
plot: Plot = Plot()
|
||||
plugins: dict[str, dict[str, Any]] = {}
|
||||
survey: Survey = Survey()
|
||||
version: str = get_version()
|
||||
version: str = __version__
|
||||
weather_station: dict[str, dict[str, Any]] = {}
|
||||
widgets: Widgets = Widgets()
|
||||
|
||||
|
|
26
uv.lock
generated
26
uv.lock
generated
|
@ -1,4 +1,5 @@
|
|||
version = 1
|
||||
revision = 1
|
||||
requires-python = ">=3.12"
|
||||
|
||||
[[package]]
|
||||
|
@ -276,7 +277,7 @@ name = "click"
|
|||
version = "8.1.7"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "colorama", marker = "platform_system == 'Windows'" },
|
||||
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/96/d3/f04c7bfcf5c1862a2a5b845c6b2b360488cf47af55dfa79c98f6a6bf98b5/click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de", size = 336121 }
|
||||
wheels = [
|
||||
|
@ -425,6 +426,18 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/d5/50/83c593b07763e1161326b3b8c6686f0f4b0f24d5526546bee538c89837d6/decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186", size = 9073 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dunamai"
|
||||
version = "1.23.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "packaging" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/06/4e/a5c8c337a1d9ac0384298ade02d322741fb5998041a5ea74d1cd2a4a1d47/dunamai-1.23.0.tar.gz", hash = "sha256:a163746de7ea5acb6dacdab3a6ad621ebc612ed1e528aaa8beedb8887fccd2c4", size = 44681 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/21/4c/963169386309fec4f96fd61210ac0a0666887d0fb0a50205395674d20b71/dunamai-1.23.0-py3-none-any.whl", hash = "sha256:a0906d876e92441793c6a423e16a4802752e723e9c9a5aabdc5535df02dbe041", size = 26342 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ecdsa"
|
||||
version = "0.19.0"
|
||||
|
@ -538,7 +551,6 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "gisaf-backend"
|
||||
version = "0.0.0"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "aiopath" },
|
||||
|
@ -587,6 +599,7 @@ mqtt = [
|
|||
[package.dev-dependencies]
|
||||
dev = [
|
||||
{ name = "asyncpg-stubs" },
|
||||
{ name = "dunamai" },
|
||||
{ name = "ipdb" },
|
||||
{ name = "pandas-stubs" },
|
||||
{ name = "pretty-errors" },
|
||||
|
@ -599,17 +612,17 @@ dev = [
|
|||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "aiomqtt", marker = "extra == 'all'", specifier = ">=1.2.1" },
|
||||
{ name = "aiomqtt", marker = "extra == 'mqtt'", specifier = ">=1.2.1" },
|
||||
{ name = "aiopath", specifier = ">=0.7.1" },
|
||||
{ name = "aiosqlite", specifier = ">=0.19.0" },
|
||||
{ name = "apscheduler", specifier = ">=3.10.4" },
|
||||
{ name = "asyncpg", specifier = ">=0.28.0" },
|
||||
{ name = "contextily", marker = "extra == 'all'", specifier = ">=1.4.0" },
|
||||
{ name = "contextily", marker = "extra == 'contextily'", specifier = ">=1.4.0" },
|
||||
{ name = "fastapi", specifier = ">=0.111" },
|
||||
{ name = "geoalchemy2", specifier = ">=0.14.2" },
|
||||
{ name = "geopandas", specifier = ">=1.0.1" },
|
||||
{ name = "gisaf-backend", extras = ["contextily"], marker = "extra == 'all'" },
|
||||
{ name = "gisaf-backend", extras = ["mqtt"], marker = "extra == 'all'" },
|
||||
{ name = "httpx", specifier = ">=0.28.1" },
|
||||
{ name = "itsdangerous", specifier = ">=2.1.2" },
|
||||
{ name = "matplotlib", specifier = ">=3.8.3" },
|
||||
|
@ -633,10 +646,12 @@ requires-dist = [
|
|||
{ name = "uvicorn", specifier = ">=0.23.2" },
|
||||
{ name = "websockets", specifier = ">=12.0" },
|
||||
]
|
||||
provides-extras = ["all", "contextily", "mqtt"]
|
||||
|
||||
[package.metadata.requires-dev]
|
||||
dev = [
|
||||
{ name = "asyncpg-stubs", specifier = ">=0.29.1" },
|
||||
{ name = "dunamai", specifier = ">=1.23.0" },
|
||||
{ name = "ipdb", specifier = ">=0.13.13" },
|
||||
{ name = "pandas-stubs", specifier = ">=2.1.4.231218" },
|
||||
{ name = "pretty-errors", specifier = ">=1.2.25" },
|
||||
|
@ -1207,6 +1222,7 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/ce/ac/5b1ea50fc08a9df82de7e1771537557f07c2632231bbab652c7e22597908/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bb89f0a835bcfc1d42ccd5f41f04870c1b936d8507c6df12b7737febc40f0909", size = 2822712 },
|
||||
{ url = "https://files.pythonhosted.org/packages/c4/fc/504d4503b2abc4570fac3ca56eb8fed5e437bf9c9ef13f36b6621db8ef00/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f0c2d907a1e102526dd2986df638343388b94c33860ff3bbe1384130828714b1", size = 2920155 },
|
||||
{ url = "https://files.pythonhosted.org/packages/b2/d1/323581e9273ad2c0dbd1902f3fb50c441da86e894b6e25a73c3fda32c57e/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f8157bed2f51db683f31306aa497311b560f2265998122abe1dce6428bd86567", size = 2959356 },
|
||||
{ url = "https://files.pythonhosted.org/packages/08/50/d13ea0a054189ae1bc21af1d85b6f8bb9bbc5572991055d70ad9006fe2d6/psycopg2_binary-2.9.10-cp313-cp313-win_amd64.whl", hash = "sha256:27422aa5f11fbcd9b18da48373eb67081243662f9b46e6fd07c3eb46e4535142", size = 2569224 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1786,7 +1802,7 @@ name = "tzlocal"
|
|||
version = "5.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "tzdata", marker = "platform_system == 'Windows'" },
|
||||
{ name = "tzdata", marker = "sys_platform == 'win32'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/04/d3/c19d65ae67636fe63953b20c2e4a8ced4497ea232c43ff8d01db16de8dc0/tzlocal-5.2.tar.gz", hash = "sha256:8d399205578f1a9342816409cc1e46a93ebd5755e39ea2d85334bea911bf0e6e", size = 30201 }
|
||||
wheels = [
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue