diff --git a/.github/workflows/auto_tests.yml b/.github/workflows/auto_tests.yml
index ffb6478f..31c5d379 100644
--- a/.github/workflows/auto_tests.yml
+++ b/.github/workflows/auto_tests.yml
@@ -36,9 +36,10 @@ jobs:
- name: Run tests
run: |
cd server
- pipenv run pytest -v --cov=mergin mergin/tests
+ pipenv run pytest -v --cov=mergin --cov-report=lcov mergin/tests
- # - name: Coveralls
- # uses: coverallsapp/github-action@v2
- # with:
- # base-path: server
+ - name: Coveralls
+ uses: coverallsapp/github-action@v2
+ with:
+ base-path: server
+ format: lcov
diff --git a/.prod.env b/.prod.env
index 3089f3a4..da2011a0 100644
--- a/.prod.env
+++ b/.prod.env
@@ -14,10 +14,6 @@ LOCAL_PROJECTS=/data
#MAINTENANCE_FILE=os.path.join(LOCAL_PROJECTS, 'MAINTENANCE') # locking file when backups are created
MAINTENANCE_FILE=/data/MAINTENANCE
-#PROXY_FIX=True
-
-#PUBLIC_DIR=os.path.join(config_dir, os.pardir, 'build', 'static')
-
#SECRET_KEY=NODEFAULT
SECRET_KEY=fixme
@@ -110,7 +106,11 @@ MAIL_USERNAME=fixme
#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=1
+# where geodiff lib copies working files
+#GEODIFF_WORKING_DIR=$LOCAL_PROJECTS/geodiff_tmp
+GEODIFF_WORKING_DIR=/data/geodiff
# celery
@@ -133,6 +133,8 @@ CELERYD_CONCURRENCY=2
#CELERYD_PREFETCH_MULTIPLIER=4
+#CELERY_ROUTES={} # split tasks into separate queues
+
# various life times
#CLOSED_ACCOUNT_EXPIRATION=5 # time in days after user closed his account to all projects and files are permanently deleted
@@ -167,8 +169,13 @@ GLOBAL_STORAGE=10737418240
# GLOBAL_ADMIN False
-# Gunicorn server socket
+# toggle registration form to create new users
+#USER_SELF_REGISTRATION=False
+
+# what type of server is running, e.g. community edition or enterprise edition
+#SERVER_TYPE=ce
+# Gunicorn server socket
PORT=5000
GEVENT_WORKER=True
diff --git a/docker-compose.yml b/docker-compose.yml
index 4be2b490..f38ca2dd 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -27,6 +27,7 @@ services:
user: 901:999
volumes:
- ./projects:/data
+ - ./server/entrypoint.sh:/app/entrypoint.sh
env_file:
- .prod.env
depends_on:
@@ -38,8 +39,11 @@ services:
celery-beat:
image: lutraconsulting/merginmaps-backend:2024.2.2
container_name: celery-beat
+ restart: always
env_file:
- .prod.env
+ volumes:
+ - ./server/entrypoint.sh:/app/entrypoint.sh
depends_on:
- redis
- server-gunicorn
@@ -49,8 +53,13 @@ services:
celery-worker:
image: lutraconsulting/merginmaps-backend:2024.2.2
container_name: celery-worker
+ restart: always
+ user: 901:999
env_file:
- .prod.env
+ volumes:
+ - ./projects:/data
+ - ./server/entrypoint.sh:/app/entrypoint.sh
depends_on:
- redis
- server-gunicorn
@@ -72,6 +81,8 @@ services:
image: nginxinc/nginx-unprivileged:1.25.5
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:
diff --git a/nginx.conf b/nginx.conf
index 539d086a..ed4512ad 100644
--- a/nginx.conf
+++ b/nginx.conf
@@ -49,6 +49,6 @@ server {
location /download/ {
internal;
- alias /data; # we need to mount data from mergin server here
+ alias /data/; # we need to mount data from mergin server here
}
}
diff --git a/server/mergin/config.py b/server/mergin/config.py
index c17d8dc3..d43df6b1 100644
--- a/server/mergin/config.py
+++ b/server/mergin/config.py
@@ -10,17 +10,15 @@
class Configuration(object):
+ # flask/connexion variables
DEBUG = config("FLASK_DEBUG", default=False, cast=bool)
TESTING = config("TESTING", default=False, cast=bool)
SECRET_KEY = config("SECRET_KEY")
- PROXY_FIX = config("PROXY_FIX", default=True, cast=bool)
- SWAGGER_UI = config(
- "SWAGGER_UI", default=False, cast=bool
- ) # to enable swagger UI console (for tests only)
- VERSION = config("VERSION", default=get_version())
- PUBLIC_DIR = config(
- "PUBLIC_DIR", default=os.path.join(config_dir, os.pardir, "build", "static")
- )
+ # to enable swagger UI console (for tests only)
+ SWAGGER_UI = config("SWAGGER_UI", default=False, cast=bool)
+ # expiration time in seconds
+ WTF_CSRF_TIME_LIMIT = config("WTF_CSRF_TIME_LIMIT", default=3600 * 24, cast=int)
+ WTF_CSRF_ENABLED = config("WTF_CSRF_ENABLED", default=True, cast=bool)
# Mergin DB related
SQLALCHEMY_TRACK_MODIFICATIONS = config(
@@ -43,11 +41,6 @@ class Configuration(object):
f"postgresql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_DATABASE}?application_name={DB_APPLICATION_NAME}",
)
- WTF_CSRF_TIME_LIMIT = config(
- "WTF_CSRF_TIME_LIMIT", default=3600 * 24, cast=int
- ) # in seconds
- WTF_CSRF_ENABLED = config("WTF_CSRF_ENABLED", default=True, cast=bool)
-
# for flask mail
MAIL_SERVER = config("MAIL_SERVER", default="localhost")
MAIL_PORT = config("MAIL_PORT", default=587, cast=int)
@@ -97,12 +90,16 @@ class Configuration(object):
GLOBAL_WRITE = config("GLOBAL_WRITE", default=False, cast=bool)
GLOBAL_ADMIN = config("GLOBAL_ADMIN", default=False, cast=bool)
- SERVER_TYPE = config("SERVER_TYPE", default="")
# can users create their own account or is it reserved for superuser only
USER_SELF_REGISTRATION = config("USER_SELF_REGISTRATION", default=False, cast=bool)
+
# build hash number
BUILD_HASH = config("BUILD_HASH", default="")
+ # backend version
+ VERSION = config("VERSION", default=get_version())
+ SERVER_TYPE = config("SERVER_TYPE", default="ce")
+
# whether to run flask app with gevent worker type in gunicorn
# using gevent type of worker impose some requirements on code, e.g. to be greenlet safe, custom timeouts
GEVENT_WORKER = config("GEVENT_WORKER", default=False, cast=bool)
diff --git a/server/mergin/sync/commands.py b/server/mergin/sync/commands.py
index 327bc560..a07d5966 100644
--- a/server/mergin/sync/commands.py
+++ b/server/mergin/sync/commands.py
@@ -75,13 +75,13 @@ def download(project_name, version, directory): # pylint: disable=W0612
os.mkdir(directory)
files = pv.files
for f in files:
- project.storage.restore_versioned_file(f["path"], version)
- f_dir = os.path.dirname(f["path"])
+ project.storage.restore_versioned_file(f.path, version)
+ f_dir = os.path.dirname(f.path)
if f_dir:
os.makedirs(os.path.join(directory, f_dir), exist_ok=True)
shutil.copy(
- os.path.join(project.storage.project_dir, f["location"]),
- os.path.join(directory, f["path"]),
+ os.path.join(project.storage.project_dir, f.location),
+ os.path.join(directory, f.path),
)
print("Project downloaded successfully")
diff --git a/web-app/packages/lib/src/modules/project/components/ProjectsTable.vue b/web-app/packages/lib/src/modules/project/components/ProjectsTable.vue
index 1fffa5ba..85c02b5b 100644
--- a/web-app/packages/lib/src/modules/project/components/ProjectsTable.vue
+++ b/web-app/packages/lib/src/modules/project/components/ProjectsTable.vue
@@ -50,7 +50,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-MerginMaps-Commercial
}"
>
{{ slotProps.data.namespace }} /{{ slotProps.data.namespace }} / {{ slotProps.data.name }}