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

-
Join our community chat
and ask questions!
+
Join our community chat
and ask questions!

## 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",