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.name }}