Skip to content

Commit

Permalink
Merge pull request #385 from MerginMaps/develop
Browse files Browse the repository at this point in the history
Release 2025.2.1
  • Loading branch information
MarcelGeo authored Feb 26, 2025
2 parents 8105541 + db495d6 commit 9666044
Show file tree
Hide file tree
Showing 27 changed files with 462 additions and 161 deletions.
2 changes: 1 addition & 1 deletion development.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ Watching the type definitions is also useful to pick up any changes to imports o

## Running locally in a docker composition

If you want to run the whole stack locally, you can use the docker. Docker will build the images from yout local files and run the services.
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
# Run the docker composition with the current Dockerfiles
Expand Down
3 changes: 2 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,13 @@ services:
restart: always
depends_on:
- server-gunicorn
user: 101:999
links:
- db
networks:
- merginmaps
proxy:
image: nginxinc/nginx-unprivileged:1.25.5
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
Expand Down
2 changes: 1 addition & 1 deletion nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ server {
# we don't want nginx trying to do something clever with
# redirects, we set the Host: header above already.
proxy_redirect off;
proxy_pass http://merginmaps-web;
proxy_pass http://merginmaps-web:8080;
}

# proxy to backend
Expand Down
2 changes: 1 addition & 1 deletion server/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ RUN apt-get update -y && \
python3-pip \
python3-setuptools \
iputils-ping \
gcc build-essential binutils cmake extra-cmake-modules libsqlite3-mod-spatialite && \
gcc build-essential binutils cmake extra-cmake-modules libsqlite3-mod-spatialite libmagic1 && \
rm -rf /var/lib/apt/lists/*


Expand Down
1 change: 1 addition & 0 deletions server/Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ shapely = "==2.0.6"
psycogreen = "==1.0.2"
importlib-metadata = "==8.4.0" # https://github.com/pallets/flask/issues/4502
typing_extensions = "==4.12.2"
python-magic = "==0.4.27"
# requirements for development on windows
colorama = "==0.4.5"

Expand Down
132 changes: 68 additions & 64 deletions server/Pipfile.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 1 addition & 4 deletions server/mergin/auth/api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -201,14 +201,10 @@ paths:
schema:
type: object
required:
- username
- email
- password
- confirm
properties:
username:
type: string
example: john.doe
email:
type: string
format: email
Expand Down Expand Up @@ -686,6 +682,7 @@ paths:
description: User info
content:
application/json:
# TODO: fix this to match the ma.SQLAlchemy schema or use UserDetail schema
schema:
$ref: "#/components/schemas/UserInfo"
"401":
Expand Down
3 changes: 2 additions & 1 deletion server/mergin/auth/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,8 @@ def register_user(): # pylint: disable=W0613,W0612
from ..celery import send_email_async

form = UserRegistrationForm()
if form.validate():
form.username.data = User.generate_username(form.email.data)
if form.validate_on_submit():
user = User.create(form.username.data, form.email.data, form.password.data)
user_created.send(user, source="admin")
token = generate_confirmation_token(
Expand Down
6 changes: 4 additions & 2 deletions server/mergin/auth/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

from ..app import db
from ..sync.models import ProjectUser
from ..sync.utils import get_user_agent, get_ip, get_device_id
from ..sync.utils import get_user_agent, get_ip, get_device_id, is_reserved_word


class User(db.Model):
Expand Down Expand Up @@ -200,7 +200,9 @@ def generate_username(cls, email: str) -> Optional[str]:
# remove forbidden chars
username = re.sub(
r"[\@\#\$\%\^\&\*\(\)\{\}\[\]\?\'\"`,;\:\+\=\~\\\/\|\<\>]", "", username
)
).ljust(4, "0")
# additional check for reserved words
username = f"{username}0" if is_reserved_word(username) else username
# check if we already do not have existing usernames
suffix = db.session.execute(
text(
Expand Down
2 changes: 1 addition & 1 deletion server/mergin/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ def _check_server(): # pylint: disable=W0612
"No contact email set. Please set CONTACT_EMAIL environment variable",
)
else:
click.secho(f"Base URL of server is {base_url}", fg="green")
click.secho(f"Your contact email is {contact_email}.", fg="green")

tables = db.engine.table_names()
if not tables:
Expand Down
3 changes: 2 additions & 1 deletion server/mergin/sync/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@

def project_name_validation(name: str) -> str | None:
"""Check whether project name is valid"""
if not name.strip():
name = name.strip() if name is not None else name
if not name:
return "Project name cannot be empty"
errors = [
has_valid_characters(name),
Expand Down
2 changes: 1 addition & 1 deletion server/mergin/sync/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1121,7 +1121,7 @@ class SyncFailuresHistory(db.Model):
error_type = db.Column(
db.String, index=True
) # e.g. push_start, push_finish, push_lost
error_details = db.Column(db.String, index=True)
error_details = db.Column(db.String)
timestamp = db.Column(db.DateTime(), default=datetime.utcnow, index=True)
user_id = db.Column(
db.Integer, db.ForeignKey("user.id", ondelete="SET NULL"), nullable=True
Expand Down
21 changes: 18 additions & 3 deletions server/mergin/sync/public_api_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
import binascii
import functools
import json
import mimetypes
import os
import logging
from dataclasses import asdict
from typing import Dict
from urllib.parse import quote
import uuid
from datetime import datetime

import psycopg2
from blinker import signal
from connexion import NoContent, request
Expand Down Expand Up @@ -87,6 +87,9 @@
get_project_path,
get_device_id,
is_valid_path,
is_supported_type,
is_supported_extension,
get_mimetype,
)
from .errors import StorageLimitHit
from ..utils import format_time_delta
Expand Down Expand Up @@ -406,7 +409,7 @@ def download_project_file(
if not is_binary(abs_path):
mime_type = "text/plain"
else:
mime_type = mimetypes.guess_type(abs_path)[0]
mime_type = get_mimetype(abs_path)
resp.headers["Content-Type"] = mime_type
resp.headers["Content-Disposition"] = "attachment; filename={}".format(
quote(os.path.basename(file).encode("utf-8"))
Expand Down Expand Up @@ -813,7 +816,16 @@ def project_push(namespace, project_name):
if not all(ele.path != item.path for ele in project.files):
abort(400, f"File {item.path} has been already uploaded")
if not is_valid_path(item.path):
abort(400, f"File {item.path} contains invalid characters.")
abort(
400,
f"Unsupported file name detected: {item.path}. Please remove the invalid characters.",
)
if not is_supported_extension(item.path):
abort(
400,
f"Unsupported file type detected: {item.path}. "
f"Please remove the file or try compressing it into a ZIP file before uploading",
)

# changes' files must be unique
changes_files = [
Expand Down Expand Up @@ -1042,6 +1054,9 @@ def push_finish(transaction_id):
)
corrupted_files.append(f.path)
continue
if not is_supported_type(dest_file):
logging.info(f"Rejecting blacklisted file: {dest_file}")
abort(400, f"Unsupported file type detected: {f.path}")

if expected_size != os.path.getsize(dest_file):
logging.error(
Expand Down
Loading

0 comments on commit 9666044

Please sign in to comment.