Correct functions for creating new users, confirming email, resetting password and forgot password.

This commit is contained in:
Josako
2024-08-21 14:59:56 +02:00
parent 6219d11e56
commit 9757830bc4
20 changed files with 291 additions and 43 deletions

View File

@@ -10,10 +10,11 @@ from flask_jwt_extended import JWTManager
from flask_session import Session from flask_session import Session
from flask_wtf import CSRFProtect from flask_wtf import CSRFProtect
# from .utils.key_encryption import JosKMSClient from .utils.nginx_utils import prefixed_url_for
from .utils.simple_encryption import SimpleEncryption from .utils.simple_encryption import SimpleEncryption
from .utils.minio_utils import MinioClient from .utils.minio_utils import MinioClient
# Create extensions # Create extensions
db = SQLAlchemy() db = SQLAlchemy()
migrate = Migrate() migrate = Migrate()

View File

@@ -126,7 +126,7 @@ class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
user_name = db.Column(db.String(80), unique=True, nullable=False) user_name = db.Column(db.String(80), unique=True, nullable=False)
email = db.Column(db.String(255), unique=True, nullable=False) email = db.Column(db.String(255), unique=True, nullable=False)
password = db.Column(db.String(255), nullable=False) password = db.Column(db.String(255), nullable=True)
first_name = db.Column(db.String(80), nullable=False) first_name = db.Column(db.String(80), nullable=False)
last_name = db.Column(db.String(80), nullable=False) last_name = db.Column(db.String(80), nullable=False)
active = db.Column(db.Boolean) active = db.Column(db.Boolean)

View File

@@ -1,6 +1,7 @@
from flask import current_app, render_template from flask import current_app, render_template
from flask_mailman import EmailMessage from flask_mailman import EmailMessage
from itsdangerous import URLSafeTimedSerializer from itsdangerous import URLSafeTimedSerializer
import socket
from common.utils.nginx_utils import prefixed_url_for from common.utils.nginx_utils import prefixed_url_for
@@ -35,15 +36,66 @@ def generate_confirmation_token(email):
def send_confirmation_email(user): def send_confirmation_email(user):
current_app.logger.debug(f'Sending confirmation email to {user.email}') current_app.logger.debug(f'Sending confirmation email to {user.email}')
if not test_smtp_connection():
raise Exception("Failed to connect to SMTP server")
token = generate_confirmation_token(user.email) token = generate_confirmation_token(user.email)
confirm_url = prefixed_url_for('security_bp.confirm_email', token=token, _external=True) confirm_url = prefixed_url_for('security_bp.confirm_email', token=token, _external=True)
current_app.logger.debug(f'Confirmation URL: {confirm_url}') current_app.logger.debug(f'Confirmation URL: {confirm_url}')
html = render_template('email/activate.html', confirm_url=confirm_url) html = render_template('email/activate.html', confirm_url=confirm_url)
send_email(user.email, "Confirm your email", html) subject = "Please confirm your email"
try:
send_email(user.email, "Confirm your email", html)
current_app.logger.info(f'Confirmation email sent to {user.email}')
except Exception as e:
current_app.logger.error(f'Failed to send confirmation email to {user.email}. Error: {str(e)}')
raise
def send_reset_email(user): def send_reset_email(user):
current_app.logger.debug(f'Sending reset email to {user.email}')
token = generate_reset_token(user.email) token = generate_reset_token(user.email)
reset_url = prefixed_url_for('security_bp.reset_password', token=token, _external=True) reset_url = prefixed_url_for('security_bp.reset_password', token=token, _external=True)
current_app.logger.debug(f'Reset URL: {reset_url}')
html = render_template('email/reset_password.html', reset_url=reset_url) html = render_template('email/reset_password.html', reset_url=reset_url)
send_email(user.email, "Reset Your Password", html) subject = "Reset Your Password"
try:
send_email(user.email, "Reset Your Password", html)
current_app.logger.info(f'Reset email sent to {user.email}')
except Exception as e:
current_app.logger.error(f'Failed to send reset email to {user.email}. Error: {str(e)}')
raise
def test_smtp_connection():
try:
current_app.logger.info(f"Attempting to resolve google.com...")
google_ip = socket.gethostbyname('google.com')
current_app.logger.info(f"Successfully resolved google.com to {google_ip}")
except Exception as e:
current_app.logger.error(f"Failed to resolve google.com: {str(e)}")
try:
smtp_server = current_app.config['MAIL_SERVER']
current_app.logger.info(f"Attempting to resolve {smtp_server}...")
smtp_ip = socket.gethostbyname(smtp_server)
current_app.logger.info(f"Successfully resolved {smtp_server} to {smtp_ip}")
except Exception as e:
current_app.logger.error(f"Failed to resolve {smtp_server}: {str(e)}")
try:
smtp_server = current_app.config['MAIL_SERVER']
smtp_port = current_app.config['MAIL_PORT']
sock = socket.create_connection((smtp_server, smtp_port), timeout=10)
sock.close()
current_app.logger.info(f"Successfully connected to SMTP server {smtp_server}:{smtp_port}")
return True
except Exception as e:
current_app.logger.error(f"Failed to connect to SMTP server: {str(e)}")
return False

View File

@@ -3,6 +3,7 @@ from datetime import timedelta
import redis import redis
from common.utils.prompt_loader import load_prompt_templates from common.utils.prompt_loader import load_prompt_templates
from eveai_app.views.security_forms import ResetPasswordForm
basedir = path.abspath(path.dirname(__file__)) basedir = path.abspath(path.dirname(__file__))
@@ -41,6 +42,11 @@ class Config(object):
SECURITY_POST_LOGIN_VIEW = '/user/tenant_overview' SECURITY_POST_LOGIN_VIEW = '/user/tenant_overview'
SECURITY_RECOVERABLE = True SECURITY_RECOVERABLE = True
SECURITY_EMAIL_SENDER = "eveai_super@flow-it.net" SECURITY_EMAIL_SENDER = "eveai_super@flow-it.net"
SECURITY_EMAIL_SUBJECT_PASSWORD_RESET = 'Reset Your Password'
SECURITY_EMAIL_SUBJECT_PASSWORD_NOTICE = 'Your Password Has Been Reset'
SECURITY_EMAIL_PLAINTEXT = False
SECURITY_EMAIL_HTML = True
SECURITY_RESET_PASSWORD_FORM = ResetPasswordForm
# Ensure Flask-Security-Too is handling CSRF tokens when behind a proxy # Ensure Flask-Security-Too is handling CSRF tokens when behind a proxy
SECURITY_CSRF_PROTECT_MECHANISMS = ['session'] SECURITY_CSRF_PROTECT_MECHANISMS = ['session']
@@ -123,6 +129,15 @@ class Config(object):
"LLM": {"name": "LLM", "description": "Algorithm using information integrated in the used LLM"} "LLM": {"name": "LLM", "description": "Algorithm using information integrated in the used LLM"}
} }
# flask-mailman settings
MAIL_SERVER = environ.get('MAIL_SERVER')
MAIL_PORT = int(environ.get('MAIL_PORT', 465))
MAIL_USE_TLS = False
MAIL_USE_SSL = True
MAIL_USERNAME = environ.get('MAIL_USERNAME')
MAIL_PASSWORD = environ.get('MAIL_PASSWORD')
MAIL_DEFAULT_SENDER = ('eveAI Admin', MAIL_USERNAME)
class DevConfig(Config): class DevConfig(Config):
DEVELOPMENT = True DEVELOPMENT = True
@@ -138,15 +153,6 @@ class DevConfig(Config):
SQLALCHEMY_DATABASE_URI = f'postgresql+pg8000://{DB_USER}:{DB_PASS}@{DB_HOST}:5432/{DB_NAME}' SQLALCHEMY_DATABASE_URI = f'postgresql+pg8000://{DB_USER}:{DB_PASS}@{DB_HOST}:5432/{DB_NAME}'
SQLALCHEMY_BINDS = {'public': SQLALCHEMY_DATABASE_URI} SQLALCHEMY_BINDS = {'public': SQLALCHEMY_DATABASE_URI}
# flask-mailman settings
MAIL_SERVER = 'mail.flow-it.net'
MAIL_PORT = 587
MAIL_USE_TLS = True
MAIL_USE_SSL = False
MAIL_DEFAULT_SENDER = ('eveAI Admin', 'eveai_admin@flow-it.net')
MAIL_USERNAME = environ.get('MAIL_USERNAME')
MAIL_PASSWORD = environ.get('MAIL_PASSWORD')
# Define the nginx prefix used for the specific apps # Define the nginx prefix used for the specific apps
EVEAI_APP_LOCATION_PREFIX = '/admin' EVEAI_APP_LOCATION_PREFIX = '/admin'
EVEAI_CHAT_LOCATION_PREFIX = '/chat' EVEAI_CHAT_LOCATION_PREFIX = '/chat'

View File

@@ -19,7 +19,9 @@ x-common-variables: &common-variables
SECRET_KEY: '97867c1491bea5ee6a8e8436eb11bf2ba6a69ff53ab1b17ecba450d0f2e572e1' SECRET_KEY: '97867c1491bea5ee6a8e8436eb11bf2ba6a69ff53ab1b17ecba450d0f2e572e1'
SECURITY_PASSWORD_SALT: '228614859439123264035565568761433607235' SECURITY_PASSWORD_SALT: '228614859439123264035565568761433607235'
MAIL_USERNAME: eveai_super@flow-it.net MAIL_USERNAME: eveai_super@flow-it.net
MAIL_PASSWORD: '$6xsWGbNtx$CFMQZqc*' MAIL_PASSWORD: '$$6xsWGbNtx$$CFMQZqc*'
MAIL_SERVER: mail.flow-it.net
MAIL_PORT: 465
OPENAI_API_KEY: 'sk-proj-8R0jWzwjL7PeoPyMhJTZT3BlbkFJLb6HfRB2Hr9cEVFWEhU7' OPENAI_API_KEY: 'sk-proj-8R0jWzwjL7PeoPyMhJTZT3BlbkFJLb6HfRB2Hr9cEVFWEhU7'
GROQ_API_KEY: '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' GROQ_API_KEY: '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'
ANTHROPIC_API_KEY: 'sk-ant-api03-c2TmkzbReeGhXBO5JxNH6BJNylRDonc9GmZd0eRbrvyekec2' ANTHROPIC_API_KEY: 'sk-ant-api03-c2TmkzbReeGhXBO5JxNH6BJNylRDonc9GmZd0eRbrvyekec2'

View File

@@ -11,7 +11,7 @@
x-common-variables: &common-variables x-common-variables: &common-variables
DB_HOST: bswnz4.stackhero-network.com DB_HOST: bswnz4.stackhero-network.com
DB_USER: luke_skywalker DB_USER: luke_skywalker
DB_PASS: 2MK&1rHmWEydE2rFuJLq*ls%tdkPAk2 DB_PASS: '2MK&1rHmWEydE2rFuJLq*ls%tdkPAk2'
DB_NAME: eveai DB_NAME: eveai
DB_PORT: '5945' DB_PORT: '5945'
FLASK_ENV: production FLASK_ENV: production
@@ -20,6 +20,8 @@ x-common-variables: &common-variables
SECURITY_PASSWORD_SALT: '166448071751628781809462050022558634074' SECURITY_PASSWORD_SALT: '166448071751628781809462050022558634074'
MAIL_USERNAME: 'evie_admin@askeveai.com' MAIL_USERNAME: 'evie_admin@askeveai.com'
MAIL_PASSWORD: 's5D%R#y^v!s&6Z^i0k&' MAIL_PASSWORD: 's5D%R#y^v!s&6Z^i0k&'
MAIL_SERVER: mail.askeveai.com
MAIL_PORT: 465
REDIS_USER: eveai REDIS_USER: eveai
REDIS_PASS: 'jHliZwGD36sONgbm0fc6SOpzLbknqq4RNF8K' REDIS_PASS: 'jHliZwGD36sONgbm0fc6SOpzLbknqq4RNF8K'
REDIS_URL: 8bciqc.stackhero-network.com REDIS_URL: 8bciqc.stackhero-network.com

View File

@@ -10,6 +10,7 @@ from common.extensions import (db, migrate, bootstrap, security, mail, login_man
minio_client, simple_encryption) minio_client, simple_encryption)
from common.models.user import User, Role, Tenant, TenantDomain from common.models.user import User, Role, Tenant, TenantDomain
import common.models.interaction import common.models.interaction
from common.utils.nginx_utils import prefixed_url_for
from config.logging_config import LOGGING from config.logging_config import LOGGING
from common.utils.security import set_tenant_session_data from common.utils.security import set_tenant_session_data
from .errors import register_error_handlers from .errors import register_error_handlers

View File

@@ -0,0 +1,13 @@
<!doctype html>
<html>
<head>
<title>Reset Your Password</title>
</head>
<body>
<p>Hi,</p>
<p>You requested a password reset for your EveAI account. Click the link below to reset your password:</p>
<p><a href="{{ reset_url }}">Reset Password</a></p>
<p>If you did not request a password reset, please ignore this email.</p>
<p>Thanks,<br>The EveAI Team</p>
</body>
</html>

View File

@@ -0,0 +1,13 @@
<!doctype html>
<html>
<head>
<title>Reset Your Password</title>
</head>
<body>
<p>Hi,</p>
<p>You requested a password reset for your EveAI account. Click the link below to reset your password:</p>
<p><a href="{{ reset_link }}">Reset Password</a></p>
<p>If you did not request a password reset, please ignore this email.</p>
<p>Thanks,<br>The EveAI Team</p>
</body>
</html>

View File

@@ -9,10 +9,16 @@
{% include "security/_messages.html" %} {% include "security/_messages.html" %}
<form action="{{ url_for_security('forgot_password') }}" method="post" name="forgot_password_form"> <form action="{{ url_for_security('forgot_password') }}" method="post" name="forgot_password_form">
{{ forgot_password_form.hidden_tag() }} {{ forgot_password_form.hidden_tag() }}
<p>
{{ forgot_password_form.email.label }}<br>
{{ forgot_password_form.email(size=80) }}
</p>
<p>{{ forgot_password_form.submit() }}</p>
{{ render_form_errors(forgot_password_form) }} {{ render_form_errors(forgot_password_form) }}
{{ render_field_with_errors(forgot_password_form.email) }}
{{ render_field_errors(forgot_password_form.csrf_token) }} {{ render_field_errors(forgot_password_form.csrf_token) }}
{{ render_field(forgot_password_form.submit) }}
</form> </form>
{% include "security/_menu.html" %} <!-- {% include "security/_menu.html" %}-->
{% endblock content %} {% endblock content %}

View File

@@ -14,10 +14,7 @@
{{ login_user_form.password.label }}<br> {{ login_user_form.password.label }}<br>
{{ login_user_form.password(size=80) }} {{ login_user_form.password(size=80) }}
</p> </p>
{# <p>#}
{# {{ login_user_form.remember_me }}#}
{# {{ login_user_form.remember_me.label }}#}
{# </p>#}
<p>{{ login_user_form.submit() }}</p> <p>{{ login_user_form.submit() }}</p>
</form> </form>

View File

@@ -1,12 +1,12 @@
{% extends "security/base.html" %} {% extends "security/base.html" %}
{% from "macros.html" import render_field %} {% from "macros.html" import render_field %}
{% block title %} {{ _fsdomain('Reset password') }} {% endblock %} {% block title %} {{ _fsdomain('Reset AskEveAI password') }} {% endblock %}
{% block content_title %} {{ _fsdomain('Reset password') }} {% endblock %} {% block content_title %} {{ _fsdomain('Reset AskEveAI password') }} {% endblock %}
{% block content_description %}An email will be sent to you with instructions.{% endblock %} {% block content_description %}An email will be sent to you with instructions.{% endblock %}
{% block content %} {% block content %}
{# {% include "security/_messages.html" %}#} {# {% include "security/_messages.html" %}#}
{# <form action="{{ url_for_security('reset_password', token=reset_password_token) }}" method="post" name="reset_password_form">#} {# <form action="{{ prefixed_url_for('security.reset_password', token=reset_password_token) }}" method="post" name="reset_password_form">#}
<form action="" method="post"> <form action="" method="post">
{{ reset_password_form.hidden_tag() }} {{ reset_password_form.hidden_tag() }}
{# {{ render_form_errors(reset_password_form) }}#} {# {{ render_form_errors(reset_password_form) }}#}

View File

@@ -12,6 +12,7 @@
<div class="form-group mt-3"> <div class="form-group mt-3">
<button type="submit" name="action" value="edit_user" class="btn btn-primary">Edit Selected User</button> <button type="submit" name="action" value="edit_user" class="btn btn-primary">Edit Selected User</button>
<button type="submit" name="action" value="resend_confirmation_email" class="btn btn-secondary">Resend Confirmation Email</button> <button type="submit" name="action" value="resend_confirmation_email" class="btn btn-secondary">Resend Confirmation Email</button>
<button type="submit" name="action" value="send_password_reset_email" class="btn btn-secondary">Send Password Reset Email</button>
<button type="submit" name="action" value="reset_uniquifier" class="btn btn-secondary">Reset Uniquifier</button> <button type="submit" name="action" value="reset_uniquifier" class="btn btn-secondary">Reset Uniquifier</button>
<!-- Additional buttons can be added here for other actions --> <!-- Additional buttons can be added here for other actions -->
</div> </div>

View File

@@ -2,6 +2,10 @@ from flask import current_app
from flask_wtf import FlaskForm from flask_wtf import FlaskForm
from wtforms import PasswordField, SubmitField, StringField from wtforms import PasswordField, SubmitField, StringField
from wtforms.validators import DataRequired, Length, Email, NumberRange, Optional, EqualTo from wtforms.validators import DataRequired, Length, Email, NumberRange, Optional, EqualTo
from flask_security.forms import ForgotPasswordForm
from flask_security.utils import send_mail, config_value
from common.utils.nginx_utils import prefixed_url_for
class SetPasswordForm(FlaskForm): class SetPasswordForm(FlaskForm):
@@ -18,4 +22,4 @@ class RequestResetForm(FlaskForm):
class ResetPasswordForm(FlaskForm): class ResetPasswordForm(FlaskForm):
password = PasswordField('Password', validators=[DataRequired()]) password = PasswordField('Password', validators=[DataRequired()])
confirm_password = PasswordField('Confirm Password', validators=[DataRequired(), EqualTo('password')]) confirm_password = PasswordField('Confirm Password', validators=[DataRequired(), EqualTo('password')])
submit = SubmitField('Reset Password') submit = SubmitField('Reset Password')

View File

@@ -164,3 +164,5 @@ def reset_password(token):

View File

@@ -154,30 +154,20 @@ def user():
form.tenant_id.data = session.get('tenant').get('id') # It is only possible to create users for the session tenant form.tenant_id.data = session.get('tenant').get('id') # It is only possible to create users for the session tenant
if form.validate_on_submit(): if form.validate_on_submit():
current_app.logger.info(f"Adding User for tenant {session['tenant']['id']} ") current_app.logger.info(f"Adding User for tenant {session['tenant']['id']} ")
if form.password.data != form.confirm_password.data:
flash('Passwords do not match.', 'danger')
return render_template('user/user.html', form=form)
# Handle the required attributes
hashed_password = hash_password(form.password.data)
new_user = User(user_name=form.user_name.data, new_user = User(user_name=form.user_name.data,
email=form.email.data, email=form.email.data,
password=hashed_password,
first_name=form.first_name.data, first_name=form.first_name.data,
last_name=form.last_name.data, last_name=form.last_name.data,
valid_to=form.valid_to.data, valid_to=form.valid_to.data,
tenant_id=form.tenant_id.data tenant_id=form.tenant_id.data,
fs_uniquifier=uuid.uuid4().hex,
) )
timestamp = dt.now(tz.utc) timestamp = dt.now(tz.utc)
new_user.created_at = timestamp new_user.created_at = timestamp
new_user.updated_at = timestamp new_user.updated_at = timestamp
# Handle the relations
tenant_id = request.form.get('tenant_id')
# the_tenant = Tenant.query.get(tenant_id)
# new_user.tenant = the_tenant
# Add roles # Add roles
for role_id in form.roles.data: for role_id in form.roles.data:
the_role = Role.query.get(role_id) the_role = Role.query.get(role_id)
@@ -188,11 +178,17 @@ def user():
try: try:
db.session.add(new_user) db.session.add(new_user)
db.session.commit() db.session.commit()
security.datastore.set_uniquifier() # security.datastore.set_uniquifier(new_user)
send_confirmation_email(new_user) try:
current_app.logger.debug(f'User {new_user.id} with name {new_user.user_name} added to database' send_confirmation_email(new_user)
f'Confirmation email sent to {new_user.email}') current_app.logger.debug(f'User {new_user.id} with name {new_user.user_name} added to database'
flash('User added successfully and confirmation email sent.', 'success') f'Confirmation email sent to {new_user.email}')
flash('User added successfully and confirmation email sent.', 'success')
except Exception as e:
current_app.logger.error(f'Failed to send confirmation email to {new_user.email}. Error: {str(e)}')
flash('User added successfully, but failed to send confirmation email. '
'Please contact the administrator.', 'warning')
return redirect(prefixed_url_for('user_bp.view_users')) return redirect(prefixed_url_for('user_bp.view_users'))
except Exception as e: except Exception as e:
current_app.logger.error(f'Failed to add user with name {new_user.user_name}. Error: {str(e)}') current_app.logger.error(f'Failed to add user with name {new_user.user_name}. Error: {str(e)}')
@@ -315,6 +311,9 @@ def handle_user_action():
elif action == 'resend_confirmation_email': elif action == 'resend_confirmation_email':
send_confirmation_email(user) send_confirmation_email(user)
flash(f'Confirmation email sent to {user.email}.', 'success') flash(f'Confirmation email sent to {user.email}.', 'success')
elif action == 'send_password_reset_email':
send_reset_email(user)
flash(f'Password reset email sent to {user.email}.', 'success')
elif action == 'reset_uniquifier': elif action == 'reset_uniquifier':
reset_uniquifier(user) reset_uniquifier(user)
flash(f'Uniquifier reset for {user.user_name}.', 'success') flash(f'Uniquifier reset for {user.user_name}.', 'success')

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,74 @@
"""fs_uniquifier required / password not required for user
Revision ID: 229774547fed
Revises: a39d2e378ccf
Create Date: 2024-08-21 08:49:55.397338
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '229774547fed'
down_revision = 'a39d2e378ccf'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('roles_users', schema=None) as batch_op:
batch_op.drop_constraint('roles_users_user_id_fkey', type_='foreignkey')
batch_op.drop_constraint('roles_users_role_id_fkey', type_='foreignkey')
batch_op.create_foreign_key(None, 'user', ['user_id'], ['id'], referent_schema='public', ondelete='CASCADE')
batch_op.create_foreign_key(None, 'role', ['role_id'], ['id'], referent_schema='public', ondelete='CASCADE')
with op.batch_alter_table('tenant_domain', schema=None) as batch_op:
batch_op.drop_constraint('tenant_domain_tenant_id_fkey', type_='foreignkey')
batch_op.drop_constraint('tenant_domain_updated_by_fkey', type_='foreignkey')
batch_op.drop_constraint('tenant_domain_created_by_fkey', type_='foreignkey')
batch_op.create_foreign_key(None, 'user', ['updated_by'], ['id'], referent_schema='public')
batch_op.create_foreign_key(None, 'user', ['created_by'], ['id'], referent_schema='public')
batch_op.create_foreign_key(None, 'tenant', ['tenant_id'], ['id'], referent_schema='public')
with op.batch_alter_table('user', schema=None) as batch_op:
batch_op.alter_column('password',
existing_type=sa.VARCHAR(length=255),
nullable=True)
batch_op.alter_column('fs_uniquifier',
existing_type=sa.VARCHAR(length=255),
nullable=False)
batch_op.drop_constraint('user_tenant_id_fkey', type_='foreignkey')
batch_op.create_foreign_key(None, 'tenant', ['tenant_id'], ['id'], referent_schema='public')
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('user', schema=None) as batch_op:
batch_op.drop_constraint(None, type_='foreignkey')
batch_op.create_foreign_key('user_tenant_id_fkey', 'tenant', ['tenant_id'], ['id'])
batch_op.alter_column('fs_uniquifier',
existing_type=sa.VARCHAR(length=255),
nullable=True)
batch_op.alter_column('password',
existing_type=sa.VARCHAR(length=255),
nullable=False)
with op.batch_alter_table('tenant_domain', schema=None) as batch_op:
batch_op.drop_constraint(None, type_='foreignkey')
batch_op.drop_constraint(None, type_='foreignkey')
batch_op.drop_constraint(None, type_='foreignkey')
batch_op.create_foreign_key('tenant_domain_created_by_fkey', 'user', ['created_by'], ['id'])
batch_op.create_foreign_key('tenant_domain_updated_by_fkey', 'user', ['updated_by'], ['id'])
batch_op.create_foreign_key('tenant_domain_tenant_id_fkey', 'tenant', ['tenant_id'], ['id'])
with op.batch_alter_table('roles_users', schema=None) as batch_op:
batch_op.drop_constraint(None, type_='foreignkey')
batch_op.drop_constraint(None, type_='foreignkey')
batch_op.create_foreign_key('roles_users_role_id_fkey', 'role', ['role_id'], ['id'], ondelete='CASCADE')
batch_op.create_foreign_key('roles_users_user_id_fkey', 'user', ['user_id'], ['id'], ondelete='CASCADE')
# ### end Alembic commands ###

View File

@@ -0,0 +1,68 @@
"""Uniquifier not required when creating user
Revision ID: a39d2e378ccf
Revises: 1716099b62f0
Create Date: 2024-08-20 15:53:08.692690
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'a39d2e378ccf'
down_revision = '1716099b62f0'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('roles_users', schema=None) as batch_op:
batch_op.drop_constraint('roles_users_role_id_fkey', type_='foreignkey')
batch_op.drop_constraint('roles_users_user_id_fkey', type_='foreignkey')
batch_op.create_foreign_key(None, 'role', ['role_id'], ['id'], referent_schema='public', ondelete='CASCADE')
batch_op.create_foreign_key(None, 'user', ['user_id'], ['id'], referent_schema='public', ondelete='CASCADE')
with op.batch_alter_table('tenant_domain', schema=None) as batch_op:
batch_op.drop_constraint('tenant_domain_updated_by_fkey', type_='foreignkey')
batch_op.drop_constraint('tenant_domain_created_by_fkey', type_='foreignkey')
batch_op.drop_constraint('tenant_domain_tenant_id_fkey', type_='foreignkey')
batch_op.create_foreign_key(None, 'user', ['updated_by'], ['id'], referent_schema='public')
batch_op.create_foreign_key(None, 'user', ['created_by'], ['id'], referent_schema='public')
batch_op.create_foreign_key(None, 'tenant', ['tenant_id'], ['id'], referent_schema='public')
with op.batch_alter_table('user', schema=None) as batch_op:
batch_op.alter_column('fs_uniquifier',
existing_type=sa.VARCHAR(length=255),
nullable=True)
batch_op.drop_constraint('user_tenant_id_fkey', type_='foreignkey')
batch_op.create_foreign_key(None, 'tenant', ['tenant_id'], ['id'], referent_schema='public')
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('user', schema=None) as batch_op:
batch_op.drop_constraint(None, type_='foreignkey')
batch_op.create_foreign_key('user_tenant_id_fkey', 'tenant', ['tenant_id'], ['id'])
batch_op.alter_column('fs_uniquifier',
existing_type=sa.VARCHAR(length=255),
nullable=False)
with op.batch_alter_table('tenant_domain', schema=None) as batch_op:
batch_op.drop_constraint(None, type_='foreignkey')
batch_op.drop_constraint(None, type_='foreignkey')
batch_op.drop_constraint(None, type_='foreignkey')
batch_op.create_foreign_key('tenant_domain_tenant_id_fkey', 'tenant', ['tenant_id'], ['id'])
batch_op.create_foreign_key('tenant_domain_created_by_fkey', 'user', ['created_by'], ['id'])
batch_op.create_foreign_key('tenant_domain_updated_by_fkey', 'user', ['updated_by'], ['id'])
with op.batch_alter_table('roles_users', schema=None) as batch_op:
batch_op.drop_constraint(None, type_='foreignkey')
batch_op.drop_constraint(None, type_='foreignkey')
batch_op.create_foreign_key('roles_users_user_id_fkey', 'user', ['user_id'], ['id'], ondelete='CASCADE')
batch_op.create_foreign_key('roles_users_role_id_fkey', 'role', ['role_id'], ['id'], ondelete='CASCADE')
# ### end Alembic commands ###

View File

@@ -53,6 +53,10 @@ http {
index index.html index.htm; index index.html index.htm;
} }
location /reset {
rewrite ^/reset(.*)$ /admin/reset$1 permanent;
}
location /static/ { location /static/ {
alias /etc/nginx/static/; alias /etc/nginx/static/;
} }