Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
a02388f
Initial setup and cleaup for admin-app and admin-lib:
MarcelGeo Aug 9, 2024
17a2fc8
Merge pull request #262 from MerginMaps/initial-admin-redesign
MarcelGeo Aug 14, 2024
55e0900
Merge remote-tracking branch 'origin/develop' into develop-redesign-a…
MarcelGeo Sep 13, 2024
c47b364
Improve searching + added reation date to User
MarcelGeo Sep 13, 2024
658f113
Added:
MarcelGeo Sep 13, 2024
63e9548
Fix lint
MarcelGeo Sep 13, 2024
32c933b
Merge remote-tracking branch 'origin/develop' into develop-redesign-a…
MarcelGeo Sep 16, 2024
559a073
Merge branch 'develop-redesign-admin' into redesign-admin-users
MarcelGeo Sep 16, 2024
b0e18a8
Update filtering for projects in admin
MarcelGeo Sep 17, 2024
57ddcc4
Update projects
MarcelGeo Sep 17, 2024
a9e547b
Fix tests for unify tests
MarcelGeo Sep 17, 2024
7bb6539
Fix items instead users
MarcelGeo Sep 17, 2024
701c70e
Fix tests for projects items
MarcelGeo Sep 17, 2024
ba825f0
Fix finding by worksapce
MarcelGeo Sep 17, 2024
3a4cbe7
Merge branch 'redesign-admin-users' into redesign-admin-projects
MarcelGeo Sep 17, 2024
eff8d8b
Merge pull request #287 from MerginMaps/redesign-admin-users
MarcelGeo Sep 18, 2024
127f074
Disable editable public private
MarcelGeo Sep 18, 2024
c063721
Merge pull request #289 from MerginMaps/redesign-admin-projects
MarcelGeo Sep 18, 2024
6c80e90
Added:
MarcelGeo Sep 20, 2024
2aba981
Updates for latest server version
MarcelGeo Sep 20, 2024
92d49c8
Cleanup of not necessary components
MarcelGeo Sep 20, 2024
160ee4d
cleanup of lib-vue2
MarcelGeo Sep 20, 2024
6daddde
Added worksapce id to paginated endpoint
MarcelGeo Sep 20, 2024
b18323a
Cleanup unused i18n
MarcelGeo Sep 20, 2024
99200de
Add licenses
MarcelGeo Sep 23, 2024
c48d772
Merge pull request #292 from MerginMaps/redesign-admin-settings
MarcelGeo Sep 23, 2024
71a2088
Merge pull request #293 from MerginMaps/redesign-admin-remove-lib-vue2
MarcelGeo Sep 23, 2024
306bb2e
Merge remote-tracking branch 'origin/develop-redesign-admin' into red…
MarcelGeo Sep 23, 2024
73e7e06
Use workspace_id from db directly
MarcelGeo Sep 23, 2024
0bebc56
Merge pull request #294 from MerginMaps/redesign-admin-add-wsid
MarcelGeo Sep 23, 2024
5982ce0
Merge remote-tracking branch 'origin/master' into develop-redesign-admin
MarcelGeo Oct 15, 2024
6348a84
Initial changes for proect detail files and versions
MarcelGeo Oct 16, 2024
83b47aa
Create project detail subpages
MarcelGeo Oct 21, 2024
bc5e005
Added:
MarcelGeo Oct 23, 2024
115c25d
Add cursor to table row in style (allow customize in other tables in …
MarcelGeo Oct 23, 2024
8e43e15
Do not show profile row if not in router
MarcelGeo Oct 23, 2024
3fdc5a5
Fix: getting changestes arguments in right order
MarcelGeo Oct 23, 2024
09e6d36
Added:
MarcelGeo Oct 23, 2024
80ceeb2
Added:
MarcelGeo Oct 23, 2024
6a4402a
Router for admin app upgrades
MarcelGeo Oct 23, 2024
e7a50bd
Move check for updates to main app vue to check it only once in session
MarcelGeo Oct 23, 2024
672d0d8
Upgrade of vue / dev deps
MarcelGeo Oct 24, 2024
40e3ab8
Update vue / dev dependencies
MarcelGeo Oct 24, 2024
e37de8d
Remove eslint standard vue plugin - no need for current setup
MarcelGeo Oct 24, 2024
27a9e6c
Merge pull request #304 from MerginMaps/redesign-project-detail
MarcelGeo Oct 24, 2024
e09f5db
Fix eslint things
MarcelGeo Oct 24, 2024
d9b1023
Fix eslint sidebar item
MarcelGeo Oct 24, 2024
17cf6fe
change env of esbuild
MarcelGeo Oct 24, 2024
9a01602
Merge remote-tracking branch 'origin/develop-redesign-admin' into dep…
MarcelGeo Oct 24, 2024
ba40659
Merge pull request #305 from MerginMaps/deps-up
MarcelGeo Oct 24, 2024
fcaea9f
Added ability to handle anchor links in sidebar
MarcelGeo Oct 25, 2024
06c1117
added sidebar footer to admin
MarcelGeo Oct 28, 2024
4ba7b29
Merge pull request #306 from MerginMaps/redesign-admin-sidebar-link
MarcelGeo Oct 28, 2024
c6f98b1
Fix: add link to project version view instead query params
MarcelGeo Nov 7, 2024
b1d085a
Make accordion smaller
MarcelGeo Nov 7, 2024
8b6e8ec
Allow searching users based on first name and last name
MarcelGeo Nov 8, 2024
f091e73
Addressing comments for admin redesign @tomasMizera
MarcelGeo Nov 8, 2024
d618ec1
Introduce ENABLE_SUPERADMIN_ASSIGNMENT variable
MarcelGeo Nov 11, 2024
8c4915c
Deactivate advanced part of account detail for current user
MarcelGeo Nov 11, 2024
3a1cac4
Remove prints
MarcelGeo Nov 11, 2024
45444a0
Merge pull request #322 from MerginMaps/redesign-final-1
MarcelGeo Nov 11, 2024
65b4ac8
Merge
MarcelGeo Nov 11, 2024
e49e011
Merge pull request #323 from MerginMaps/gh#2610-enable-assignment-var…
MarcelGeo Nov 11, 2024
0c9ad90
Fix imports after merge conflicts
MarcelGeo Nov 11, 2024
65a5c67
Merge remote-tracking branch 'origin/develop' into develop-redesign-a…
MarcelGeo Nov 11, 2024
809d93b
Fix merge conflicts projects_query
MarcelGeo Nov 11, 2024
92c2eb3
Add searching text fo projects
MarcelGeo Nov 12, 2024
61448dc
Merge pull request #327 from MerginMaps/redesign-admin-search-text
MarcelGeo Nov 12, 2024
93d9624
Merge remote-tracking branch 'origin/develop' into develop-redesign-a…
MarcelGeo Nov 12, 2024
190bec5
Merge remote-tracking branch 'origin/develop' into develop-redesign-a…
MarcelGeo Nov 12, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 2 additions & 0 deletions server/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

Configuration.SERVER_TYPE = "ce"
Configuration.USER_SELF_REGISTRATION = False

application = create_app(
[
"DOCS_URL",
Expand All @@ -37,6 +38,7 @@
"GLOBAL_ADMIN",
"GLOBAL_READ",
"GLOBAL_WRITE",
"ENABLE_SUPERADMIN_ASSIGNMENT",
]
)
register_stats(application)
Expand Down
9 changes: 7 additions & 2 deletions server/mergin/auth/api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ paths:
count:
type: integer
example: 10
users:
items:
type: array
items:
$ref: "#/components/schemas/User"
Expand Down Expand Up @@ -670,6 +670,11 @@ components:
type: string
format: date-time
example: 2023-07-30T08:47:58Z
registration_date:
nullable: true
type: string
format: date-time
example: 2023-07-30T08:47:58Z
profile:
$ref: "#/components/schemas/UserProfile"
PaginatedUsers:
Expand Down Expand Up @@ -839,4 +844,4 @@ components:
- editor
- reader
- guest
example: reader
example: reader
22 changes: 16 additions & 6 deletions server/mergin/auth/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import pytz
from datetime import datetime, timedelta
from connexion import NoContent
from sqlalchemy import func, desc, asc
from sqlalchemy import func, desc, asc, or_
from sqlalchemy.sql.operators import is_
from flask import request, current_app, jsonify, abort, render_template
from flask_login import login_user, logout_user, current_user
Expand All @@ -23,7 +23,7 @@
user_account_closed,
)
from .bearer import encode_token
from .models import User, LoginHistory
from .models import User, LoginHistory, UserProfile
from .schemas import UserSchema, UserSearchSchema, UserProfileSchema, UserInfoSchema
from .forms import (
LoginForm,
Expand Down Expand Up @@ -408,11 +408,17 @@ def update_user(username): # pylint: disable=W0613,W0612
form = UserForm.from_json(request.json)
if not form.validate_on_submit():
return jsonify(form.errors), 400
if request.json.get("is_admin") is not None and not current_app.config.get(
"ENABLE_SUPERADMIN_ASSIGNMENT"
):
abort(400, "Unable to assign super admin role")

user = User.query.filter_by(username=username).first_or_404("User not found")
form.update_obj(user)

# remove inactive since flag for ban or re-activation
user.inactive_since = None

db.session.add(user)
db.session.commit()
return jsonify(UserSchema().dump(user))
Expand Down Expand Up @@ -449,13 +455,17 @@ def get_paginated_users(

:rtype: Dict[str: List[User], str: Integer]
"""
users = User.query.filter(
users = User.query.join(UserProfile).filter(
is_(User.username.ilike("deleted_%"), False) | is_(User.active, True)
)

if like:
attr = User.email if "@" in like else User.username
users = users.filter(attr.ilike(f"%{like}%"))
users = users.filter(
User.username.ilike(f"%{like}%")
| User.email.ilike(f"%{like}%")
| UserProfile.first_name.ilike(f"%{like}%")
| UserProfile.last_name.ilike(f"%{like}%")
)

if descending and order_by:
users = users.order_by(desc(User.__table__.c[order_by]))
Expand All @@ -467,7 +477,7 @@ def get_paginated_users(

result_users = UserSchema(many=True).dump(result)

data = {"users": result_users, "total": total}
data = {"items": result_users, "count": total}
return data, 200


Expand Down
1 change: 1 addition & 0 deletions server/mergin/auth/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ class Meta:
"verified_email",
"profile",
"scheduled_removal",
"registration_date",
)
load_instance = True

Expand Down
4 changes: 4 additions & 0 deletions server/mergin/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ class Configuration(object):

# build hash number
BUILD_HASH = config("BUILD_HASH", default="")
# Allow changing access to admin panel
ENABLE_SUPERADMIN_ASSIGNMENT = config(
"ENABLE_SUPERADMIN_ASSIGNMENT", default=True, cast=bool
)
# backend version
VERSION = config("VERSION", default=get_version())
SERVER_TYPE = config("SERVER_TYPE", default="ce")
29 changes: 11 additions & 18 deletions server/mergin/sync/private_api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -183,20 +183,13 @@ paths:
- $ref: "#/components/parameters/Page"
- $ref: "#/components/parameters/PerPage"
- $ref: "#/components/parameters/OrderParams"
- name: name
- name: like
in: query
description: Filter projects by name with ilike pattern
description: Filter projects by name or workspace name with ilike pattern
required: false
schema:
type: string
example: survey
- name: workspace
in: query
description: Filter projects by workspace with ilike pattern
required: false
schema:
type: string
example: my-workspace
responses:
"200":
description: List of projects
Expand All @@ -209,7 +202,7 @@ paths:
type: integer
description: Total number of all projects
example: 20
projects:
items:
type: array
items:
$ref: "#/components/schemas/ProjectListItem"
Expand Down Expand Up @@ -292,7 +285,7 @@ paths:
id:
type: string
format: uuid
example: 'd4ecda97-0595-40af-892c-e7522de70bd2'
example: "d4ecda97-0595-40af-892c-e7522de70bd2"
name:
type: string
example: survey
Expand Down Expand Up @@ -460,7 +453,7 @@ components:
- detail
UsersLimitHit:
allOf:
- $ref: '#/components/schemas/CustomError'
- $ref: "#/components/schemas/CustomError"
type: object
properties:
rejected_emails:
Expand All @@ -473,7 +466,7 @@ components:
example:
code: UsersLimitHit
detail: Maximum number of people in this workspace is reached. Please upgrade your subscription to add more people (UsersLimitHit)
rejected_emails: [ rejected@example.com ]
rejected_emails: [rejected@example.com]
users_quota: 6
ProjectAccessRequestList:
type: array
Expand All @@ -488,7 +481,7 @@ components:
project_id:
type: string
format: uuid
example: 'd4ecda97-0595-40af-892c-e7522de70bd2'
example: "d4ecda97-0595-40af-892c-e7522de70bd2"
project_name:
type: string
example: survey
Expand Down Expand Up @@ -619,25 +612,25 @@ components:
nullable: false
items:
type: string
example: [ john.doe ]
example: [john.doe]
writersnames:
type: array
nullable: false
items:
type: string
example: [ john.doe ]
example: [john.doe]
editorsnames:
type: array
nullable: false
items:
type: string
example: [ john.doe ]
example: [john.doe]
readersnames:
nullable: false
type: array
items:
type: string
example: [ john.doe ]
example: [john.doe]
public:
type: boolean
example: true
Expand Down
8 changes: 3 additions & 5 deletions server/mergin/sync/private_api_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,10 +185,8 @@ def list_namespace_project_access_requests(


@auth_required(permissions=["admin"])
def list_projects(
page, per_page, order_params=None, name=None, workspace=None
): # noqa: E501
projects = current_app.ws_handler.projects_query(name, workspace)
def list_projects(page, per_page, order_params=None, like=None): # noqa: E501
projects = current_app.ws_handler.projects_query(like)
# do not fetch from db what is not needed
projects = projects.options(
defer(Project.storage_params),
Expand All @@ -211,7 +209,7 @@ def list_projects(
result = projects.paginate(page, per_page).items
total = projects.paginate(page, per_page).total
data = AdminProjectSchema(many=True).dump(result)
data = {"projects": data, "count": total}
data = {"items": data, "count": total}
return data, 200


Expand Down
3 changes: 2 additions & 1 deletion server/mergin/sync/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,8 @@ class Meta:
class AdminProjectSchema(ma.SQLAlchemyAutoSchema):
id = fields.UUID(attribute="Project.id")
name = fields.Str(attribute="Project.name")
namespace = fields.Method("_workspace_name")
workspace = fields.Method("_workspace_name")
workspace_id = fields.Int(attribute="Project.workspace_id")
version = fields.Function(
lambda obj: ProjectVersion.to_v_name(obj.Project.latest_version)
)
Expand Down
13 changes: 7 additions & 6 deletions server/mergin/sync/workspace.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,16 +269,17 @@ def monthly_contributors_count():
.count()
)

def projects_query(self, name=None, workspace=None):
def projects_query(self, like: str = None):
ws = self.factory_method()
query = db.session.query(
Project, literal(ws.name).label("workspace_name")
Project,
literal(ws.name).label("workspace_name"),
).filter(Project.storage_params.isnot(None))

if name:
query = query.filter(Project.name.ilike(f"%{name}%"))
if workspace:
query = query.filter(literal(ws.name).ilike(f"%{workspace}%"))
if like:
query = query.filter(
Project.name.ilike(f"%{like}%") | literal(ws.name).ilike(f"%{like}%")
)
return query

@staticmethod
Expand Down
40 changes: 30 additions & 10 deletions server/mergin/tests/test_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,25 @@ def test_api_user_profile(client):
def test_update_user(client):
login_as_admin(client)
user = User.query.filter_by(username="mergin").first()
data = {"active": True, "is_admin": True}
resp = client.patch(
url_for("/.mergin_auth_controller_update_user", username=user.username),
data=json.dumps(data),
headers=json_headers,
)
assert resp.status_code == 200
assert user.active
assert user.is_admin

client.application.config["ENABLE_SUPERADMIN_ASSIGNMENT"] = False
data = {"active": False, "is_admin": False}
resp = client.patch(
url_for("/.mergin_auth_controller_update_user", username=user.username),
data=json.dumps(data),
headers=json_headers,
)
assert resp.status_code == 400
assert user.active
data = {"active": False}
resp = client.patch(
url_for("/.mergin_auth_controller_update_user", username=user.username),
Expand All @@ -421,6 +440,7 @@ def test_update_user(client):
assert resp.status_code == 200
assert not user.active

client.application.config["ENABLE_SUPERADMIN_ASSIGNMENT"] = True
user.is_admin = False
db.session.add(user)
db.session.commit()
Expand Down Expand Up @@ -694,27 +714,27 @@ def test_paginate_users(client):
url = "/app/admin/users?page=1&per_page=10"
# get 5 users (default + 5 new added - 1 deleted & inactive)
resp = client.get(url)
list_of_usernames = [user["username"] for user in resp.json["users"]]
assert resp.json["total"] == 5
assert resp.json["users"][0]["username"] == "mergin"
list_of_usernames = [user["username"] for user in resp.json["items"]]
assert resp.json["count"] == 5
assert resp.json["items"][0]["username"] == "mergin"
assert user_inactive.username in list_of_usernames
assert deleted_active.username in list_of_usernames
assert deleted_inactive.username not in list_of_usernames
# order by username
resp = client.get(url + "&order_by=username")
assert resp.json["total"] == 5
assert resp.json["users"][0]["username"] == "alice"
assert resp.json["count"] == 5
assert resp.json["items"][0]["username"] == "alice"
# exact match with username
resp = client.get(url + "&like=bob")
assert resp.json["total"] == 1
assert resp.json["users"][0]["username"] == "bob"
assert resp.json["count"] == 1
assert resp.json["items"][0]["username"] == "bob"
# ilike search with email
resp = client.get(url + "&like=@mergin.com")
assert resp.json["total"] == 5
assert resp.json["count"] == 5
# exact search by email
resp = client.get(url + "&like=alice@mergin.com")
assert resp.json["total"] == 1
assert resp.json["users"][0]["username"] == "alice"
assert resp.json["count"] == 1
assert resp.json["items"][0]["username"] == "alice"
# invalid paging
assert client.get("/app/admin/users?page=2&per_page=10").status_code == 404

Expand Down
Loading