diff --git a/.gitignore b/.gitignore
index 4b7c63da..94220ca0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
# demo data
-projects/
+projects*/
+data/
mergin_db
logs
@@ -15,6 +16,8 @@ deps/
venv/
.vscode
+# production env
+.prod.env
# generated documentation
gen
diff --git a/LICENSES/CLA-signed-list.md b/LICENSES/CLA-signed-list.md
index 4c36d6bf..627b7e57 100644
--- a/LICENSES/CLA-signed-list.md
+++ b/LICENSES/CLA-signed-list.md
@@ -19,4 +19,4 @@ C/ My company has custom contribution contract with Lutra Consulting Ltd. or I a
* lavor, 26th April 2023
* luxusko, 25th August 2023
* jozef-budac, 30th January 2024
-* fernandinand, 13th March 2025
\ No newline at end of file
+* fernandinand, 13th March 2025
diff --git a/README.md b/README.md
index eba1e8f8..6fbc30d3 100644
--- a/README.md
+++ b/README.md
@@ -74,7 +74,7 @@ Admin users can enter the admin interface available at `/admin` URL which provid
### Contributing
-Contributions are welcomed! You can set up development environment by following a guide in [development.md](./development.md). Before you create your first pull request, we kindly ask you to sign the CLA with your GitHub user name and date [here](LICENSES/CLA-signed-list.md).
+Contributions are welcomed! You can set up development environment by following a guide in [development.md](./deployment/community/development.md). Before you create your first pull request, we kindly ask you to sign the CLA with your GitHub user name and date [here](LICENSES/CLA-signed-list.md).
## Documentation
@@ -86,14 +86,14 @@ If you'd like to contribute and improve the documentation, visit https://github.
If you need support, a custom deployment, extending the service capabilities and new features do not hesitate to contact us on info@lutraconsulting.co.uk
-
+
## Developers
Contributions are welcome!
-More information for developers can be found in the dedicated [development](development.md) page.
+More information for developers can be found in the dedicated [development](./deployment/community/development.md) page.
Client side modules:
- [Python](https://github.com/MerginMaps/python-api-client) client library + CLI
diff --git a/deployment/common/entrypoint.sh b/deployment/common/entrypoint.sh
new file mode 100755
index 00000000..4b7d4888
--- /dev/null
+++ b/deployment/common/entrypoint.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# Copyright (C) Lutra Consulting Limited
+#
+# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-MerginMaps-Commercial
+
+# make sure all files created by gunicorn (mergin server) have proper permissions
+umask 0027
+
+# Settings passed to gunicorn have the following order of precedence
+# (tested using --workers):
+#
+# 1. Command-line (highest)
+# 2. Environment variable
+# 3. File referenced by --config (lowest)
+#
+# We store a base config in config.py and override things as needed
+# using the environment variable GUNICORN_CMD_ARGS.
+
+exec sh -c "$@"
diff --git a/nginx.conf b/deployment/common/nginx.conf
similarity index 100%
rename from nginx.conf
rename to deployment/common/nginx.conf
diff --git a/deployment/common/set_permissions.sh b/deployment/common/set_permissions.sh
new file mode 100644
index 00000000..3d0d6c59
--- /dev/null
+++ b/deployment/common/set_permissions.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+# Changes permissions recursively at folder and files level from a provided path
+# ARGS:
+# 1 - The target path
+
+
+set -e
+
+export MERGIN_DIR=$1
+sudo mkdir -p $MERGIN_DIR
+sudo find $MERGIN_DIR -type f -exec sudo chmod 640 {} \;
+sudo find $MERGIN_DIR -type d -exec sudo chmod 750 {} \;
+sudo find $MERGIN_DIR -type d -exec sudo chmod g+s {} \;
+sudo chown -R 901:999 $MERGIN_DIR
diff --git a/ssl-proxy.conf b/deployment/common/ssl-proxy.conf
similarity index 100%
rename from ssl-proxy.conf
rename to deployment/common/ssl-proxy.conf
diff --git a/.dev.env b/deployment/community/.dev.env
similarity index 100%
rename from .dev.env
rename to deployment/community/.dev.env
diff --git a/.prod.env b/deployment/community/.env.template
similarity index 98%
rename from .prod.env
rename to deployment/community/.env.template
index b6b3dc2a..b6a37508 100644
--- a/.prod.env
+++ b/deployment/community/.env.template
@@ -29,12 +29,11 @@ TEMP_DIR=/data/tmp
#DB_APPLICATION_NAME=mergin
-#DB_DATABASE=postgres
+#DB_DATABASE=mergin
-#DB_HOST=localhost
DB_HOST=db
-#DB_PASSWORD=postgres
+DB_PASSWORD=postgres
#DB_POOL_MAX_OVERFLOW=10 # max_overflow set to SQLAlchemy default https://docs.sqlalchemy.org/en/14/core/engines.html
diff --git a/deployment/community/README.md b/deployment/community/README.md
new file mode 100644
index 00000000..e99b90c3
--- /dev/null
+++ b/deployment/community/README.md
@@ -0,0 +1,16 @@
+# Mergin Maps Community Edition Deployment
+Suitable for Ubuntu servers, one node deployment using docker compose and system nginx as a reverse proxy.
+
+> [!IMPORTANT]
+> You need to have Docker installed on your system.
+> If you don't have, follow the official [documentation](https://docs.docker.com/engine/install/)
+
+Then modify [docker-compose file](docker-compose.yml) and create environment file `.prod.env` from `.env.template`. Details about configuration can be find in [docs](https://merginmaps.com/docs/server/install/).
+
+```shell
+cp .env.template .prod.env
+```
+
+Next step is to create data directory for mergin maps `projects` with proper permissions. Should you prefer a different location, please do search and replace it in config files (`.prod.env`, `docker-compose.yml`). Make sure your volume is large enough since mergin maps keeps all projects files, their history and also needs some space for temporary processing.
+
+For more details about deployment please check [docs](https://merginmaps.com/docs/server/install/#deployment).
diff --git a/docker-compose.dev.yml b/deployment/community/docker-compose.dev.yml
similarity index 79%
rename from docker-compose.dev.yml
rename to deployment/community/docker-compose.dev.yml
index 8f39a76a..9aa77bd8 100644
--- a/docker-compose.dev.yml
+++ b/deployment/community/docker-compose.dev.yml
@@ -1,10 +1,10 @@
-version: "3.7"
+
services:
- server-gunicorn:
- image: server-gunicorn
+ server:
+ image: server
build:
- context: ./server
+ context: ../../server
dockerfile: Dockerfile
env_file:
- .prod.env
@@ -12,7 +12,7 @@ services:
celery-beat:
image: celery-beat
build:
- context: ./server
+ context: ../../server
dockerfile: Dockerfile
env_file:
- .prod.env
@@ -20,7 +20,7 @@ services:
celery-worker:
image: celery-worker
build:
- context: ./server
+ context: ../../server
dockerfile: Dockerfile
env_file:
- .prod.env
@@ -28,7 +28,7 @@ services:
web:
image: merginmaps-frontend
build:
- context: ./web-app
+ context: ../../web-app
dockerfile: Dockerfile
maildev:
image: maildev/maildev
diff --git a/docker-compose.yml b/deployment/community/docker-compose.yml
similarity index 80%
rename from docker-compose.yml
rename to deployment/community/docker-compose.yml
index fd19df78..9ee27549 100644
--- a/docker-compose.yml
+++ b/deployment/community/docker-compose.yml
@@ -1,6 +1,7 @@
-version: "3.7"
+
networks:
merginmaps:
+ name: mergin
services:
db:
@@ -10,8 +11,9 @@ services:
networks:
- merginmaps
environment:
+ - POSTGRES_DB=mergin
- POSTGRES_USER=postgres
- - POSTGRES_PASSWORD=postgres
+ - POSTGRES_PASSWORD=postgres # !TODO Change this and also change .prod.env $DB_PASSWORD accordingly
volumes:
- ./mergin_db:/var/lib/postgresql/data
redis:
@@ -20,14 +22,14 @@ services:
restart: always
networks:
- merginmaps
- server-gunicorn:
+ server:
image: lutraconsulting/merginmaps-backend:2025.2.2
container_name: merginmaps-server
restart: always
user: 901:999
volumes:
- ./projects:/data
- - ./server/entrypoint.sh:/app/entrypoint.sh
+ - ../common/entrypoint.sh:/app/entrypoint.sh
env_file:
- .prod.env
depends_on:
@@ -46,10 +48,10 @@ services:
- GEVENT_WORKER=0
- NO_MONKEY_PATCH=1
volumes:
- - ./server/entrypoint.sh:/app/entrypoint.sh
+ - ../common/entrypoint.sh:/app/entrypoint.sh
depends_on:
- redis
- - server-gunicorn
+ - server
command: [ "celery -A application.celery beat --loglevel=info" ]
networks:
- merginmaps
@@ -65,10 +67,10 @@ services:
- NO_MONKEY_PATCH=1
volumes:
- ./projects:/data
- - ./server/entrypoint.sh:/app/entrypoint.sh
+ - ../common/entrypoint.sh:/app/entrypoint.sh
depends_on:
- redis
- - server-gunicorn
+ - server
- celery-beat
command: [ "celery -A application.celery worker --loglevel=info" ]
networks:
@@ -78,7 +80,7 @@ services:
container_name: merginmaps-web
restart: always
depends_on:
- - server-gunicorn
+ - server
user: 101:999
links:
- db
@@ -93,8 +95,7 @@ services:
ports:
- "8080:8080"
volumes:
- - ./projects:/data # map data dir to host
- - ./nginx.conf:/etc/nginx/conf.d/default.conf
- - ./logs:/var/log/nginx/
+ - ./projects:/data # mergin maps projects data dir to host
+ - ../common/nginx.conf:/etc/nginx/conf.d/default.conf
networks:
- merginmaps
diff --git a/deployment/enterprise/.env.template b/deployment/enterprise/.env.template
new file mode 100644
index 00000000..1a4b4764
--- /dev/null
+++ b/deployment/enterprise/.env.template
@@ -0,0 +1,207 @@
+# This file should contain a full set of Mergin Maps configuration
+# definitions along with their default values
+
+FLASK_APP=application
+GEODIFF_LOGGER_LEVEL=2
+CONTACT_EMAIL=fixme
+
+
+# ALL VERSIONS #########################################################################################################
+
+#DEBUG=FLASK_DEBUG | False
+
+#LOCAL_PROJECTS=os.path.join(config_dir, os.pardir, os.pardir, 'projects') # for local storage type
+LOCAL_PROJECTS=/data
+
+#MAINTENANCE_FILE=os.path.join(LOCAL_PROJECTS, 'MAINTENANCE') # locking file when backups are created
+MAINTENANCE_FILE=/data/MAINTENANCE
+
+#PROXY_FIX=True
+
+#SECRET_KEY=NODEFAULT
+SECRET_KEY=fix-me
+
+#SWAGGER_UI=False # to enable swagger UI console (for test only)
+
+#TEMP_DIR=gettempdir() # trash dir for temp files being cleaned regularly
+TEMP_DIR=/data/tmp
+
+#TESTING=False
+
+#USER_SELF_REGISTRATION=True
+
+#VERSION=get_version()
+
+
+# Mergin DB related
+
+#DB_APPLICATION_NAME=mergin
+
+DB_HOST=db
+
+DB_PASSWORD=postgres # fixme
+
+#DB_POOL_MAX_OVERFLOW=10 # max_overflow set to SQLAlchemy default https://docs.sqlalchemy.org/en/14/core/engines.html
+
+#DB_POOL_SIZE=2
+
+#DB_POOL_TIMEOUT=300
+
+#DB_PORT=5002
+DB_PORT=5432
+
+#DB_USER=postgres
+
+#SQLALCHEMY_DATABASE_URI=postgresql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_DATABASE}?application_name={DB_APPLICATION_NAME}'
+
+#SQLALCHEMY_ENGINE_OPTIONS={'pool_size': DB_POOL_SIZE, 'max_overflow': DB_POOL_MAX_OVERFLOW, 'pool_timeout' DB_POOL_TIMEOUT}
+
+#SQLALCHEMY_TRACK_MODIFICATIONS=False
+
+
+# auth related
+
+#BEARER_TOKEN_EXPIRATION=3600 * 12 # in seconds
+
+SECURITY_BEARER_SALT=fixme
+SECURITY_EMAIL_SALT=fixme
+SECURITY_PASSWORD_SALT=fixme
+
+#WTF_CSRF_ENABLED=True
+
+#WTF_CSRF_TIME_LIMIT=3600 * 24 # in seconds
+
+
+# for flask mail
+
+#MAIL_BCC=NODEFAULT
+MAIL_BCC=fixme
+
+#MAIL_DEBUG=MAIL_SUPPRESS_SEND | False
+
+#MAIL_DEFAULT_SENDER=NODEFAULT
+MAIL_DEFAULT_SENDER=fixme
+
+#MAIL_PASSWORD=NODEFAULT
+MAIL_PASSWORD=fixme
+
+#MAIL_PORT=587
+
+#MAIL_SERVER=localhost
+MAIL_SERVER=fixme
+
+#MAIL_SUPPRESS_SEND=True
+
+#MAIL_USE_TLS=True
+
+#MAIL_USERNAME=NODEFAULT
+MAIL_USERNAME=fix-me
+
+
+# data sync
+
+#BLACKLIST='.mergin/, .DS_Store, .directory' # cast=Csv()
+
+#FILE_EXPIRATION=48 * 3600 # for clean up of old files where diffs were applied, in seconds
+
+#LOCKFILE_EXPIRATION=300 # in seconds
+
+#MAX_CHUNK_SIZE=10 * 1024 * 1024 # 10485760 in bytes
+
+#MAX_DOWNLOAD_ARCHIVE_SIZE=1024 * 1024 * 1024 # max total files size for archive download
+
+#USE_X_ACCEL=False # use nginx (in front of gunicorn) to serve files (https://www.nginx.com/resources/wiki/start/topics/examples/x-accel/)
+USE_X_ACCEL=True
+
+# celery
+
+#BROKER_URL=redis://172.17.0.1:6379/0
+BROKER_URL=redis://merginmaps-redis:6379/0
+
+#BROKER_TRANSPORT_OPTIONS={} # cast=eval
+BROKER_TRANSPORT_OPTIONS={ 'master_name': 'mymaster' }
+
+#CELERY_RESULT_BACKEND=redis://172.17.0.1:6379/0'
+CELERY_RESULT_BACKEND=redis://merginmaps-redis:6379/0
+
+#CELERY_RESULT_BACKEND_TRANSPORT_OPTIONS={} # cast=eval
+CELERY_RESULT_BACKEND_TRANSPORT_OPTIONS={ 'master_name': 'mymaster' }
+
+#CELERY_ACKS_LATE=False
+CELERY_ACKS_LATE=True
+
+# set to number of cpu
+#CELERYD_CONCURRENCY=1
+CELERYD_CONCURRENCY=2
+
+#CELERYD_PREFETCH_MULTIPLIER=4
+CELERYD_PREFETCH_MULTIPLIER=4
+
+
+# various life times
+
+#CLOSED_ACCOUNT_EXPIRATION=5 # time in days after user closed his account to all projects and files are permanently deleted
+CLOSED_ACCOUNT_EXPIRATION=1
+
+#DELETED_PROJECT_EXPIRATION=7 # lifetime of deleted project, expired project are removed permanently without restore possibility, in days
+
+#ORGANISATION_INVITATION_EXPIRATION=7 * 24 * 3600 # in seconds
+
+#PROJECT_ACCESS_REQUEST=7 * 24 * 3600
+
+#TEMP_EXPIRATION=7 # time in days after files are permanently deleted
+
+#TRANSFER_EXPIRATION=7 * 24 * 3600 # in seconds
+
+
+# for links generated in emails
+
+#MERGIN_BASE_URL=http://localhost:5000
+MERGIN_BASE_URL=fixme
+
+#MERGIN_LOGO_URL= # for link to logo in emails
+MERGIN_LOGO_URL=fixme
+
+# global workspace related bits - ignored in non-CE versions
+# GLOBAL_WORKSPACE mergin
+
+# GLOBAL_STORAGE 1024 * 1024 * 1024
+
+# GLOBAL_READ False
+
+# GLOBAL_WRITE False
+
+# GLOBAL_ADMIN False
+
+# EE ##############################################################################################################
+
+# workspaces related bits
+# WORKSPACE_STORAGE_SIZE 100 * 1024 * 1024
+
+# WORKSPACE_INVITATION_EXPIRATION 7 days
+
+# PROJECT_TRANSFER_EXPIRATION 7 days
+
+# WORKSPACE_EXPIRATION = 7 days
+
+# USER_SELF_REGISTRATION True
+
+# USER_WORKSPACES_ALLOWED True
+
+# MAPS #################################################################################################################
+
+MAPS_ENABLED=True
+
+OVERVIEW_DATA=/overviews
+
+VECTOR_TILES_URL=https://tiles-ee.merginmaps.com/data/default/{z}/{x}/{y}.pbf
+
+VECTOR_TILES_STYLE_URL=https://tiles-ee.merginmaps.com//styles/default.json
+
+#QGIS_EXTRACTOR_API_URL=http://mergin-qgis-extractor:8000
+
+#WMTS_SERVER_URL=http://mergin-qgis-nginx:80
+
+#QGIS_EXTRACTOR_TIMEOUT=60
+
+#OVERVIEW_MAX_FILE_SIZE=1048576 # 1MB
diff --git a/deployment/enterprise/README.md b/deployment/enterprise/README.md
new file mode 100644
index 00000000..5620c12c
--- /dev/null
+++ b/deployment/enterprise/README.md
@@ -0,0 +1,28 @@
+# Mergin Maps Enterprise Edition Deployment
+Suitable for Ubuntu servers, one node deployment using docker compose and system nginx as a reverse proxy.
+
+> [!IMPORTANT]
+> Docker images for Mergin Maps Enterprise edition are stored on a private AWS ECR repository.
+> To access them, you need a Mergin Maps Enterprise [subscription](https://merginmaps.com/pricing).
+> Please contact Mergin Maps [sales team](https://merginmaps.com/contact-sales)!
+
+## Login to Mergin Maps AWS ECR repository
+```shell
+aws ecr --region eu-west-1 get-login-password | docker login --username AWS --password-stdin 433835555346.dkr.ecr.eu-west-1.amazonaws.com
+```
+
+## Load docker images, configure and run mergin maps stack
+For running Mergin Maps you need to load local docker images (if any). Make sure you have access to Lutra's ECR repository. You can check it by running
+```
+docker pull 433835555346.dkr.ecr.eu-west-1.amazonaws.com/mergin/mergin-ee-back:2025.3.0
+```
+
+Then modify [docker-compose file](docker-compose.yml) and create environment file `.prod.env` from `.env.template`. Details about configuration can be find in [docs](https://merginmaps.com/docs/server/install/).
+
+```shell
+cp .env.template .prod.env
+```
+
+Next step is to create data directory for Mergin Maps `data` with proper permissions. Should you prefer a different location, please do search and replace it in config files (`.prod.env`, `docker-compose.yml`). Make sure your volume is large enough since Mergin Maps keeps all projects files, their history and also needs some space for temporary processing.
+
+For more details about deployment please check [docs](https://merginmaps.com/docs/server/install/#deployment).
diff --git a/deployment/enterprise/docker-compose.maps.yml b/deployment/enterprise/docker-compose.maps.yml
new file mode 100644
index 00000000..24cacb43
--- /dev/null
+++ b/deployment/enterprise/docker-compose.maps.yml
@@ -0,0 +1,42 @@
+networks:
+ mergin-net:
+ external: true
+ name: mergin-ee
+
+services:
+ qgis:
+ container_name: mergin-qgis
+ image: 433835555346.dkr.ecr.eu-west-1.amazonaws.com/mergin/qgis-server-ee:2025.1.0
+ user: 1000:999
+ networks:
+ - mergin-net
+ environment:
+ - QGIS_SERVER_PARALLEL_RENDERING=false
+ - QGIS_SERVER_MAX_THREADS=-1
+ - QGIS_SERVER_WMS_MAX_HEIGHT=1536
+ - QGIS_SERVER_WMS_MAX_WIDTH=1536
+ - QGIS_SERVER_LOG_LEVEL=2
+ volumes:
+ - ./map_data:/overviews
+ qgis_nginx:
+ container_name: mergin-qgis-nginx
+ image: nginxinc/nginx-unprivileged:1.27
+ user: 101:999
+ networks:
+ - mergin-net
+ depends_on:
+ - qgis
+ volumes:
+ - ./qgis_nginx.conf:/etc/nginx/conf.d/default.conf
+ qgis_extractor:
+ container_name: mergin-qgis-extractor
+ image: 433835555346.dkr.ecr.eu-west-1.amazonaws.com/mergin/qgis-extractor-ee:2025.1.0
+ user: 901:999
+ networks:
+ - mergin-net
+ environment:
+ - OVERVIEWS_DATA_DIR=/data
+ - MM_WMS_TILE_BUFFER=100
+ - MM_WMS_AVOID_ARTIFACTS=1
+ volumes:
+ - ./map_data:/data
diff --git a/deployment/enterprise/docker-compose.yml b/deployment/enterprise/docker-compose.yml
new file mode 100644
index 00000000..cb5084c8
--- /dev/null
+++ b/deployment/enterprise/docker-compose.yml
@@ -0,0 +1,105 @@
+name: mergin-enterprise
+networks:
+ mergin:
+ name: mergin-ee
+
+services:
+ server:
+ image: 433835555346.dkr.ecr.eu-west-1.amazonaws.com/mergin/mergin-ee-back:2025.3.0
+ container_name: merginmaps-server
+ restart: always
+ user: 901:999
+ command: ["gunicorn -w 4 --config config.py application:application"]
+ volumes:
+ - ./data:/data # map data dir to host
+ - ../common/entrypoint.sh:/app/entrypoint.sh
+ env_file:
+ - .prod.env
+ depends_on:
+ - db
+ networks:
+ - mergin
+
+ web:
+ image: 433835555346.dkr.ecr.eu-west-1.amazonaws.com/mergin/mergin-ee-front:2025.3.0
+ container_name: merginmaps-web
+ restart: always
+ depends_on:
+ - server
+ env_file:
+ - .prod.env
+ networks:
+ - mergin
+
+ proxy:
+ image: nginxinc/nginx-unprivileged:1.27
+ container_name: merginmaps-proxy
+ restart: always
+ # run nginx as built-in user but with group mergin-family for files permissions
+ user: 101:999
+ ports:
+ - "8080:8080"
+ volumes:
+ - ./data:/data # map data dir to host
+ - ../common/nginx.conf:/etc/nginx/templates/default.conf.template
+ networks:
+ - mergin
+ depends_on:
+ - web
+ - server
+
+ celery-beat:
+ image: 433835555346.dkr.ecr.eu-west-1.amazonaws.com/mergin/mergin-ee-back:2025.3.0
+ container_name: merginmaps-celery-beat
+ restart: always
+ user: 901:999
+ command: ["celery -A application.celery beat --loglevel=info"]
+ volumes:
+ - ../common/entrypoint.sh:/app/entrypoint.sh
+ env_file:
+ - .prod.env
+ depends_on:
+ - db
+ - redis
+ networks:
+ - mergin
+
+ celery-worker:
+ image: 433835555346.dkr.ecr.eu-west-1.amazonaws.com/mergin/mergin-ee-back:2025.3.0
+ container_name: merginmaps-celery-worker
+ restart: always
+ user: 901:999
+ command: ["celery -A application.celery worker --pool prefork --loglevel=info"]
+ volumes:
+ - ./data:/data # map data dir to host
+ - ./map_data:/overviews
+ - ../common/entrypoint.sh:/app/entrypoint.sh
+ env_file:
+ - .prod.env
+ depends_on:
+ - db
+ - redis
+ networks:
+ - mergin
+
+ db:
+ image: postgres:14
+ container_name: merginmaps-db
+ restart: always
+ volumes:
+ - ./mergin-db-enterprise:/var/lib/postgresql/data
+ environment:
+ - POSTGRES_DB=mergin
+ - POSTGRES_USER=postgres
+ - POSTGRES_PASSWORD=postgres # !TODO Change this and also change .prod.env $DB_PASSWORD accordingly
+ ports:
+ - 5432:5432
+ networks:
+ - mergin
+
+ redis:
+ image: redis
+ container_name: merginmaps-redis
+ restart: always
+ networks:
+ - mergin
diff --git a/deployment/enterprise/qgis_nginx.conf b/deployment/enterprise/qgis_nginx.conf
new file mode 100644
index 00000000..29c1afe4
--- /dev/null
+++ b/deployment/enterprise/qgis_nginx.conf
@@ -0,0 +1,16 @@
+server {
+ listen 80;
+ server_name _;
+
+ location / {
+ proxy_buffers 16 16k;
+ proxy_buffer_size 16k;
+ gzip off;
+ include fastcgi_params;
+ fastcgi_pass qgis:5555;
+ # Wait up to 10 seconds for the qgis-server fastcgi application
+ # to return a response.
+ fastcgi_read_timeout 10s;
+ }
+
+}
diff --git a/development.md b/development.md
index 2d76b1dd..b51c83d4 100644
--- a/development.md
+++ b/development.md
@@ -65,6 +65,9 @@ Watching the type definitions is also useful to pick up any changes to imports o
If you want to run the whole stack locally, you can use the docker. Docker will build the images from your local files and run the services.
```shell
+# Enter community edition deployment folder
+cd deployment/community/
+
# Run the docker composition with the current Dockerfiles
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d
diff --git a/server/mergin/auth/models.py b/server/mergin/auth/models.py
index 31499ad3..e697c3f5 100644
--- a/server/mergin/auth/models.py
+++ b/server/mergin/auth/models.py
@@ -212,12 +212,12 @@ def generate_username(cls, email: str) -> Optional[str]:
text(
"""
SELECT
- replace(lower(username), :username, '0')::int AS suffix
+ replace(lower(username), :username, '0')::bigint AS suffix
FROM "user"
WHERE
lower(username) = :username OR
lower(username) SIMILAR TO :username_like
- ORDER BY replace(lower(username), :username, '0')::int DESC
+ ORDER BY replace(lower(username), :username, '0')::bigint DESC
LIMIT 1;
"""
),
diff --git a/server/mergin/tests/test_auth.py b/server/mergin/tests/test_auth.py
index a5217f6a..b717b4e4 100644
--- a/server/mergin/tests/test_auth.py
+++ b/server/mergin/tests/test_auth.py
@@ -866,6 +866,10 @@ def test_username_generation(client):
user = add_user("testuser1")
assert User.generate_username("Testuser@example.com") == "testuser2"
+ # test username with crazy long int suffix
+ user = add_user("testuser13120931904")
+ assert User.generate_username("Testuser@example.com") == "testuser13120931905"
+
def test_server_usage(client):
"""Test server usage endpoint"""
diff --git a/server/mergin/version.py b/server/mergin/version.py
index efbe6d52..06f9d965 100644
--- a/server/mergin/version.py
+++ b/server/mergin/version.py
@@ -4,4 +4,4 @@
def get_version():
- return "2025.2.2"
+ return "2025.3.1"
diff --git a/server/setup.py b/server/setup.py
index fb67ae9a..68e46ca6 100644
--- a/server/setup.py
+++ b/server/setup.py
@@ -6,7 +6,7 @@
setup(
name="mergin",
- version="2025.2.2",
+ version="2025.3.1",
url="https://github.com/MerginMaps/mergin",
license="AGPL-3.0-only",
author="Lutra Consulting Limited",