Skip to content

Commit

Permalink
Merge pull request #110 from MerginMaps/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
varmar05 authored Sep 6, 2023
2 parents ae0c164 + 7f5201b commit 989d24b
Show file tree
Hide file tree
Showing 14 changed files with 51 additions and 344 deletions.
1 change: 1 addition & 0 deletions LICENSES/CLA-signed-list.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ C/ My company has custom contribution contract with Lutra Consulting Ltd. or I a
* harminius, 18th Aril 2023
* varmar05, 12th April 2023
* lavor, 26th April 2023
* luxusko, 25th August 2023
41 changes: 26 additions & 15 deletions server/mergin/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,31 @@ def update_obj(self, obj):
field.populate_obj(obj, name)


def create_simple_app() -> Flask:
from .config import Configuration

app = connexion.FlaskApp(__name__, specification_dir=os.path.join(this_dir))
flask_app = app.app

flask_app.json_encoder = FlaskJSONEncoder
flask_app.config.from_object(Configuration)
db.init_app(flask_app)
ma.init_app(flask_app)
Migrate(flask_app, db)
flask_app.connexion_app = app

@flask_app.cli.command()
def init_db():
"""Re-creates application database"""
print("Database initialization ...")
db.drop_all(bind=None)
db.create_all(bind=None)
db.session.commit()
print("Done. Tables created.")

return flask_app


def create_app(public_keys: List[str] = None) -> Flask:
"""Factory function to create Flask app instance"""
from itsdangerous import BadTimeSignature, BadSignature
Expand All @@ -119,8 +144,7 @@ def create_app(public_keys: List[str] = None) -> Flask:
from .sync.commands import add_commands
from .auth import register as register_auth

app = connexion.FlaskApp(__name__, specification_dir=os.path.join(this_dir))
app.app.json_encoder = FlaskJSONEncoder
app = create_simple_app().connexion_app

app.add_api(
"sync/public_api.yaml",
Expand All @@ -136,13 +160,9 @@ def create_app(public_keys: List[str] = None) -> Flask:
validate_responses=True,
)

app.app.config.from_object(Configuration)
app.app.config.from_object(SyncConfig)
app.app.connexion_app = app

db.init_app(app.app)
ma.init_app(app.app)
Migrate(app.app, db)
mail.init_app(app.app)
app.mail = mail
csrf.init_app(app.app)
Expand Down Expand Up @@ -388,15 +408,6 @@ def config():
cfg["server_configured"] = is_server_configured()
return jsonify(cfg), 200

@application.cli.command()
def init_db():
"""Re-creates application database"""
print("Database initialization ...")
db.drop_all(bind=None)
db.create_all(bind=None)
db.session.commit()
print("Done. Tables created.")

# append project commands (from default sync module)
add_commands(application)
return application
Expand Down
51 changes: 1 addition & 50 deletions server/mergin/auth/db_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,56 +2,9 @@
#
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-MerginMaps-Commercial

from flask import render_template, current_app
from sqlalchemy import event

from .app import force_delete_user
from .. import db
from ..auth.models import UserProfile, User


def before_user_profile_updated(mapper, connection, target):
"""Before profile updated, inform user by sending email about that profile that changed
Just send email if user want to receive notifications
"""
from ..celery import send_email_async

if target.receive_notifications and target.user.verified_email:
state = db.inspect(target)
changes = {}

for attr in state.attrs:
hist = attr.load_history()
if not hist.has_changes():
continue

before = hist.deleted[0]
after = hist.added[0]
field = attr.key

# if boolean, show Yes or No
if before is not None and isinstance(before, bool):
before = "Yes" if before is True else "No"
if after is not None and isinstance(after, bool):
after = "Yes" if after is True else "No"

profile_key = field.title().replace("_", " ")
changes[profile_key] = {"before": before, "after": after}

# inform user
if changes:
email_data = {
"subject": "Profile has been changed",
"html": render_template(
"email/profile_changed.html",
subject="Profile update",
user=target.user,
changes=changes,
),
"recipients": [target.user.email],
"sender": current_app.config["MAIL_DEFAULT_SENDER"],
}
send_email_async.delay(**email_data)
from ..auth.models import User


def permanently_delete_user(user: User):
Expand All @@ -61,10 +14,8 @@ def permanently_delete_user(user: User):


def register_events():
event.listen(UserProfile, "after_update", before_user_profile_updated)
force_delete_user.connect(permanently_delete_user)


def remove_events():
event.remove(UserProfile, "after_update", before_user_profile_updated)
force_delete_user.disconnect(permanently_delete_user)
38 changes: 1 addition & 37 deletions server/mergin/sync/db_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@
from sqlalchemy import event

from .. import db
from ..auth.models import User, UserProfile
from ..auth.models import User
from .models import Project, ProjectAccess
from .public_api_controller import project_deleted


def remove_user_references(mapper, connection, user): # pylint: disable=W0612
Expand All @@ -35,39 +34,6 @@ def filter_user(ids):
)


def project_post_delete_actions(project: Project) -> None: # pylint: disable=W0612
"""After project is deleted inform users by sending email"""
from ..celery import send_email_async

if not project.access:
return
users_ids = list(
set(project.access.owners + project.access.writers + project.access.readers)
)
users_profiles = UserProfile.query.filter(UserProfile.user_id.in_(users_ids)).all()
project_workspace = project.workspace
for profile in users_profiles:
# skip the user who triggered deletion
if profile.user.username == project.removed_by:
continue

if not (profile.receive_notifications and profile.user.verified_email):
continue

email_data = {
"subject": f'Mergin project {"/".join([project_workspace.name, project.name])} has been deleted',
"html": render_template(
"email/removed_project.html",
subject="Project deleted",
project=project,
username=profile.user.username,
),
"recipients": [profile.user.email],
"sender": current_app.config["MAIL_DEFAULT_SENDER"],
}
send_email_async.delay(**email_data)


def check(session):
if os.path.isfile(current_app.config["MAINTENANCE_FILE"]):
abort(503, "Service unavailable due to maintenance, please try later")
Expand All @@ -76,10 +42,8 @@ def check(session):
def register_events():
event.listen(User, "before_delete", remove_user_references)
event.listen(db.session, "before_commit", check)
project_deleted.connect(project_post_delete_actions)


def remove_events():
event.remove(User, "before_delete", remove_user_references)
event.remove(db.session, "before_commit", check)
project_deleted.disconnect(project_post_delete_actions)
25 changes: 0 additions & 25 deletions server/mergin/sync/private_api_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,31 +266,6 @@ def unsubscribe_project(id): # pylint: disable=W0612
project.access.unset_role(current_user.id)
db.session.add(project)
db.session.commit()
# notify owners and the user who unsubscribed
project_path = get_project_path(project)
recipients = (
UserProfile.query.filter(
UserProfile.user_id.in_(project.access.owners + [current_user.id])
)
.filter(UserProfile.receive_notifications)
.all()
)
for profile in recipients:
if not profile.user.verified_email:
continue
html = render_template(
"email/project_unsubscribe.html",
project_path=project_path,
recipient=profile.user.username,
username=current_user.username,
)
email_data = {
"subject": f"Access to mergin project {project_path} has been modified",
"html": html,
"recipients": [profile.user.email],
"sender": current_app.config["MAIL_DEFAULT_SENDER"],
}
send_email_async.delay(**email_data)
return NoContent, 200


Expand Down
47 changes: 1 addition & 46 deletions server/mergin/sync/public_api_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
from connexion import NoContent, request
from flask import (
abort,
render_template,
current_app,
send_from_directory,
jsonify,
Expand All @@ -35,7 +34,7 @@
from werkzeug.exceptions import HTTPException
from .. import db
from ..auth import auth_required
from ..auth.models import User, UserProfile
from ..auth.models import User
from .models import Project, ProjectAccess, ProjectVersion, Upload
from .schemas import (
ProjectSchema,
Expand Down Expand Up @@ -73,7 +72,6 @@
get_path_from_files,
get_project_path,
)
from ..celery import send_email_async
from .errors import StorageLimitHit
from ..utils import format_time_delta

Expand Down Expand Up @@ -644,49 +642,6 @@ def update_project(namespace, project_name): # noqa: E501 # pylint: disable=W0
db.session.add(project)
db.session.commit()

# send email notifications about changes to users
user_profiles = UserProfile.query.filter(
UserProfile.user_id.in_(list(id_diffs))
).all()
project_path = "/".join([namespace, project.name])
web_link = f"{request.url_root.strip('/')}/projects/{project_path}"
for user_profile in user_profiles:
if not (
user_profile.receive_notifications and user_profile.user.verified_email
):
continue
privileges = []
if user_profile.user.id in project.access.owners:
privileges += ["edit", "remove"]
if user_profile.user.id in project.access.writers:
privileges.append("upload")
if user_profile.user.id in project.access.readers:
privileges.append("download")
subject = "Project access modified"
if len(privileges):
html = render_template(
"email/modified_project_access.html",
subject=subject,
project=project,
user=user_profile.user,
privileges=privileges,
link=web_link,
)
else:
html = render_template(
"email/removed_project_access.html",
subject=subject,
project=project,
user=user_profile.user,
)

email_data = {
"subject": f"Access to mergin project {project_path} has been modified",
"html": html,
"recipients": [user_profile.user.email],
"sender": current_app.config["MAIL_DEFAULT_SENDER"],
}
send_email_async.delay(**email_data)
# partial success
if error:
return jsonify(**error.to_dict(), project=ProjectSchema().dump(project)), 207
Expand Down
17 changes: 0 additions & 17 deletions server/mergin/templates/email/modified_project_access.html

This file was deleted.

33 changes: 0 additions & 33 deletions server/mergin/templates/email/profile_changed.html

This file was deleted.

15 changes: 0 additions & 15 deletions server/mergin/templates/email/project_unsubscribe.html

This file was deleted.

11 changes: 0 additions & 11 deletions server/mergin/templates/email/removed_project.html

This file was deleted.

Loading

0 comments on commit 989d24b

Please sign in to comment.