Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
9ee6d64
Add example test for cli
varmar05 Jul 3, 2025
cecb04d
typo
harminius Jul 21, 2025
b591601
simplify test with sso user
harminius Jul 21, 2025
af7d422
Add normalize input
harminius Jul 24, 2025
19bb6ca
test_create_user
harminius Jul 24, 2025
82bbe17
unify create project
harminius Jul 24, 2025
1fdac01
test_download_project
harminius Jul 24, 2025
692eeec
test_remove_project
harminius Jul 24, 2025
ff5cadd
Address review
harminius Jul 28, 2025
b6ea1da
Make internal commands function accesible
harminius Jul 28, 2025
626e6cb
test statistics, test check email
harminius Jul 28, 2025
64cc502
Fix assert test_middleware
harminius Jul 28, 2025
106950b
test_check_server and test_check_permission
harminius Jul 30, 2025
b55bc68
imports
harminius Jul 30, 2025
17786e4
Review & test init-db
harminius Jul 30, 2025
18f70a5
Fix permissions & test init command
harminius Jul 30, 2025
764fc75
Double check permissions cleanup
harminius Jul 30, 2025
5cae967
mock sso user to allow login
harminius Jul 30, 2025
2058154
test_check_celery
harminius Jul 30, 2025
3736951
normalize email in init cmd
harminius Jul 31, 2025
c815094
Merge pull request #474 from MerginMaps/test_cli_commands
MarcelGeo Aug 4, 2025
efe7a80
Adjust avatar font size globally
harminius Aug 4, 2025
ff740b8
Fix avatar font-size - normal, large, xlarge
harminius Aug 12, 2025
a8f5535
add comment about custom values
harminius Aug 13, 2025
6179b1f
Merge pull request #486 from MerginMaps/avatar_font_size
MarcelGeo Aug 13, 2025
8d337e1
Merge pull request #492 from MerginMaps/master
MarcelGeo Aug 13, 2025
5384ad6
bump version to 2025.6.2
harminius Aug 13, 2025
aa88f8e
Merge pull request #493 from MerginMaps/bump_server_version_2025.6.2
MarcelGeo Aug 13, 2025
8694f1b
Improve download endpoint
harminius Aug 15, 2025
fe217cb
cleanup comments
harminius Aug 15, 2025
4c2dd77
fix datetime usage after import change
harminius Aug 15, 2025
03f62a3
improve tests readability
harminius Aug 15, 2025
ab75371
Cleanup of non necessary entrypoints
MarcelGeo Aug 15, 2025
1642bfc
Merge pull request #497 from MerginMaps/fix_download_project_archive
MarcelGeo Aug 18, 2025
fb9cbaa
Add post request to explicitely prepare download archive
harminius Aug 18, 2025
4d923d1
Fix tests
harminius Aug 19, 2025
f83bd45
cleanup
harminius Aug 19, 2025
61fa9b7
Cleanup duplicated MERGIN_BASE_URL
MarcelGeo Aug 19, 2025
1151319
Update response codes
harminius Aug 26, 2025
076202d
Add post request to explicitely prepare download archive (#499)
harminius Aug 27, 2025
ceaab9a
Make right sidebar 30rem from lagre screens
harminius Aug 28, 2025
0ac5cba
Merge pull request #501 from MerginMaps/develop
varmar05 Aug 29, 2025
cacb71b
Fix MERGIN_LOGO_URL in emails to mm default
MarcelGeo Sep 1, 2025
016a15c
catch error code and display the message
harminius Sep 1, 2025
ddd9266
Catch POST error
harminius Sep 1, 2025
6741750
Merge pull request #502 from MerginMaps/wider_sidebar
MarcelGeo Sep 1, 2025
f9e7292
Merge pull request #503 from MerginMaps/large_project_download_error
MarcelGeo Sep 1, 2025
7c50798
Merge pull request #505 from MerginMaps/develop
MarcelGeo Sep 3, 2025
8dd9959
Merge pull request #498 from MerginMaps/update-limits-for-heade-and-c…
MarcelGeo Sep 3, 2025
dff06ea
Fix typos
harminius Sep 4, 2025
cee21df
Merge pull request #507 from MerginMaps/fix_readme_links
MarcelGeo Sep 8, 2025
9828bb1
Cache last sign in date in User table
varmar05 Sep 9, 2025
d2cc128
Merge pull request #509 from MerginMaps/cache_last_login
MarcelGeo Sep 10, 2025
7c00c42
Filter diff files from files types
MarcelGeo Sep 18, 2025
4e8338a
Merge pull request #510 from MerginMaps/do-not-check-diff-mimetype
MarcelGeo Sep 22, 2025
270602f
add valid mimetype for .py files
MarcelGeo Sep 22, 2025
54781eb
added x-perl
MarcelGeo Sep 22, 2025
389c59c
Merge pull request #512 from MerginMaps/add-python-mimetype
MarcelGeo Sep 23, 2025
72ade57
Remove some types from guessing as its causing errors in qml files
MarcelGeo Sep 25, 2025
8e650d6
Merge pull request #516 from MerginMaps/revert-some-mimetypes
MarcelGeo Sep 26, 2025
4575d3e
2025.7.3
MarcelGeo Sep 30, 2025
846a396
Merge pull request #517 from MerginMaps/bump-2025.7.3
MarcelGeo Sep 30, 2025
0bddcfa
Merge pull request #511 from MerginMaps/master
MarcelGeo Sep 30, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ You are currently browsing repository for Mergin Maps web server and web client.
- 🌱 **Sharing with collaborators** - Projects can be shared with other team members
- 🏰 **Permission system** - Decide who can read, write or manage projects
- 🌈 **Web interface** - Simple user interface to view and manage projects
- ⚡️ **Fast** - Efficient sync protocol transfering data between clients and server
- ⚡️ **Fast** - Efficient sync protocol transferring data between clients and server
- 🧑‍💻 **Developer friendly** - Mergin Maps is open platform. CLI tools and client libraries are available for [Python](https://github.com/MerginMaps/python-api-client) and [C++](https://github.com/MerginMaps/cpp-api-client)
- :camera: **Sync images** - Supporting sync of photos with common cloud storage using [mergin-media-sync](https://github.com/MerginMaps/media-sync) tool
- 💽 **Sync with database** - Supporting two-way sync of data with PostGIS using [mergin-db-sync](https://github.com/MerginMaps/db-sync) tool
Expand All @@ -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](./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).
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).

## Documentation

Expand All @@ -93,7 +93,7 @@ If you need support, a custom deployment, extending the service capabilities and

Contributions are welcome!

More information for developers can be found in the dedicated [development](./deployment/community/development.md) page.
More information for developers can be found in the dedicated [development](./development.md) page.

Client side modules:
- [Python](https://github.com/MerginMaps/python-api-client) client library + CLI
Expand Down
6 changes: 3 additions & 3 deletions deployment/community/.env.template
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,10 @@ MAIL_SUPPRESS_SEND=0

#MAIL_BCC=''

#MERGIN_LOGO_URL= # for link to logo in emails
MERGIN_LOGO_URL=https://merginmaps.com/MM_logo_HORIZ_COLOR_TRANSPARENT_no_padding.png

#MAIL_DEBUG=MAIL_SUPPRESS_SEND | False



# data sync

#LOCAL_PROJECTS=os.path.join(config_dir, os.pardir, os.pardir, 'projects') # for local storage type
Expand Down Expand Up @@ -215,5 +213,7 @@ NO_MONKEY_PATCH=False

# Diagnostic logs

DIAGNOSTIC_LOGS_URL=

DIAGNOSTIC_LOGS_DIR=/diagnostic_logs

4 changes: 0 additions & 4 deletions deployment/community/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ services:
volumes:
- ./projects:/data
- ./diagnostic_logs:/diagnostic_logs
- ../common/entrypoint.sh:/app/entrypoint.sh
env_file:
- .prod.env
depends_on:
Expand All @@ -48,8 +47,6 @@ services:
environment:
- GEVENT_WORKER=0
- NO_MONKEY_PATCH=1
volumes:
- ../common/entrypoint.sh:/app/entrypoint.sh
depends_on:
- redis
- server
Expand All @@ -68,7 +65,6 @@ services:
- NO_MONKEY_PATCH=1
volumes:
- ./projects:/data
- ../common/entrypoint.sh:/app/entrypoint.sh
depends_on:
- redis
- server
Expand Down
7 changes: 3 additions & 4 deletions deployment/enterprise/.env.template
Original file line number Diff line number Diff line change
Expand Up @@ -175,11 +175,8 @@ ACCOUNT_EXPIRATION=1

# 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
MERGIN_LOGO_URL=https://merginmaps.com/MM_logo_HORIZ_COLOR_TRANSPARENT_no_padding.png

# global workspace related bits - ignored in non-CE versions
# GLOBAL_WORKSPACE mergin
Expand Down Expand Up @@ -228,6 +225,8 @@ VECTOR_TILES_STYLE_URL=https://tiles-ee.merginmaps.com//styles/default.json
### Diagnostic logs from Mobile and QGIS Plugin
DIAGNOSTIC_LOGS_DIR=/diagnostic_logs

DIAGNOSTIC_LOGS_URL=

### SSO ################################################################################################################
SSO_ENABLED=False

Expand Down
6 changes: 2 additions & 4 deletions deployment/enterprise/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ services:
volumes:
- ./data:/data # map data dir to host
- ./diagnostic_logs:/diagnostic_logs # diagnostic logs dir
- ../common/entrypoint.sh:/app/entrypoint.sh
env_file:
- .prod.env
environment:
- GUNICORN_CMD_ARGS="--limit-request-line 8190"
depends_on:
- db
networks:
Expand Down Expand Up @@ -56,8 +57,6 @@ services:
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:
Expand All @@ -75,7 +74,6 @@ services:
volumes:
- ./data:/data # map data dir to host
- ./map_data:/overviews
- ../common/entrypoint.sh:/app/entrypoint.sh
env_file:
- .prod.env
depends_on:
Expand Down
4 changes: 2 additions & 2 deletions development.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ $ yarn install
$ yarn link:dependencies # link dependencies
$ yarn build:libs # bild libraries @mergin/lib @mergin/admin-lib @mergin/lib-vue2
$ yarn dev # development client web application dev server on port 8080 (package @mergin/app)
$ yarn dev:admin # development admin appplication dev server on port 8081 (package @mergin/admin-app)
$ yarn dev:admin # development admin application dev server on port 8081 (package @mergin/admin-app)
```

If you are developing a library package (named **-lib*), it is useful to watch the library for changes instead of rebuilding it each time.
Expand Down Expand Up @@ -98,7 +98,7 @@ docker exec -it merginmaps-server flask server send-check-email --email admin@e
In docker-compose.dev.yml is started maildev/maildev image that can be used to test emails (see [https://github.com/maildev/maildev/](https://github.com/maildev/maildev/)). In localhost:1080 you can see the emails sent by the application in web interface.

### Running with remote debugger
If you want to run the application with remote debugger, you can use debug compose file with attatched source code and reload.
If you want to run the application with remote debugger, you can use debug compose file with attached source code and reload.
It starts a debugpy session on port 5678 you can attach to.

```shell
Expand Down
17 changes: 10 additions & 7 deletions server/mergin/auth/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
#
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-MerginMaps-Commercial

import sys
import click
from flask import Flask
from sqlalchemy import or_, func

from ..app import db
from .models import User, UserProfile
from ..commands import normalize_input


def add_commands(app: Flask):
Expand All @@ -17,24 +19,25 @@ def user():
pass

@user.command()
@click.argument("username")
@click.argument("username", callback=normalize_input())
@click.argument("password")
@click.option("--is-admin", is_flag=True)
@click.option("--email", required=True)
@click.option("--email", required=True, callback=normalize_input())
def create(username, password, is_admin, email): # pylint: disable=W0612
"""Create user account"""
user = User.query.filter(
or_(
func.lower(User.username) == func.lower(username),
func.lower(User.email) == func.lower(email),
func.lower(User.username) == username,
func.lower(User.email) == email,
)
).first()
).count()
if user:
print("ERROR: User already exists!\n")
return
click.secho("ERROR: User already exists", fg="red", err=True)
sys.exit(1)

user = User(username=username, passwd=password, is_admin=is_admin, email=email)
user.profile = UserProfile()
user.active = True
db.session.add(user)
db.session.commit()
click.secho("User created", fg="green")
36 changes: 36 additions & 0 deletions server/mergin/auth/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ class User(db.Model):
default=datetime.datetime.utcnow,
)

last_signed_in = db.Column(db.DateTime(), nullable=True)

__table_args__ = (
db.Index("ix_user_username", func.lower(username), unique=True),
db.Index("ix_user_email", func.lower(email), unique=True),
Expand Down Expand Up @@ -289,6 +291,7 @@ def __init__(self, user_id: int, ua: str, ip: str, device_id: Optional[str] = No
self.user_agent = ua
self.ip_address = ip
self.device_id = device_id
self.timestamp = datetime.datetime.now(tz=datetime.timezone.utc)

@staticmethod
def add_record(user_id: int, req: request) -> None:
Expand All @@ -300,4 +303,37 @@ def add_record(user_id: int, req: request) -> None:
return
lh = LoginHistory(user_id, ua, ip, device_id)
db.session.add(lh)

# cache user last login
User.query.filter_by(id=user_id).update({"last_signed_in": lh.timestamp})
db.session.commit()

@staticmethod
def get_users_last_signed_in(user_ids: list) -> dict:
"""Get users last signed in dates.
Result is also cached in User table for future use.
"""
result = (
db.session.query(
LoginHistory.user_id,
func.max(LoginHistory.timestamp).label("last_signed_in"),
)
.filter(LoginHistory.user_id.in_(user_ids))
.group_by(LoginHistory.user_id)
.all()
)

user_mapping = [
{
"id": row.user_id, # user_id as PK in User table
"last_signed_in": row.last_signed_in,
}
for row in result
]
if not user_mapping:
return {}

# cache users last signed in
db.session.bulk_update_mappings(User, user_mapping)
db.session.commit()
return {item["id"]: item["last_signed_in"] for item in user_mapping}
Loading
Loading