Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
"""add_trusted_member_fields_to_users

Revision ID: 74585af9596b
Revises: ed3dabd7833f
Create Date: 2026-01-16 19:07:47.441613

"""
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = '74585af9596b'
down_revision = 'ed3dabd7833f'
branch_labels = None
depends_on = None


def upgrade() -> None:
# Add trusted member fields to users table
# is_trusted_member: Boolean flag indicating if user can create contests
# trusted_member_request: Boolean flag indicating if user has requested trusted member status
# Check if columns exist before adding to handle partial migration scenarios
conn = op.get_bind()
inspector = sa.inspect(conn)
columns = [col['name'] for col in inspector.get_columns('users')]

if 'is_trusted_member' not in columns:
op.add_column('users', sa.Column('is_trusted_member', sa.Boolean(), nullable=False, server_default=sa.text('0')))

if 'trusted_member_request' not in columns:
op.add_column('users', sa.Column('trusted_member_request', sa.Boolean(), nullable=False, server_default=sa.text('0')))


def downgrade() -> None:
# Remove trusted member fields from users table
op.drop_column('users', 'trusted_member_request')
op.drop_column('users', 'is_trusted_member')

Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"""Add trusted_member field to users table

Revision ID: 87b208812990
Revises: ea8b4c071516
Create Date: 2026-01-16 18:28:57.235313

"""
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = '87b208812990'
down_revision = 'ea8b4c071516'
branch_labels = None
depends_on = None


def upgrade() -> None:
# Add trusted_member column with default False for existing users
# Existing users will not be trusted members by default
# Check if column exists before adding to handle partial migration scenarios
conn = op.get_bind()
inspector = sa.inspect(conn)
columns = [col['name'] for col in inspector.get_columns('users')]

if 'trusted_member' not in columns:
op.add_column('users', sa.Column('trusted_member', sa.Boolean(), nullable=False, server_default=sa.false()))


def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('users', 'trusted_member')
# ### end Alembic commands ###

Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"""merge trusted member migrations

Revision ID: 90aa0fb7e9ad
Revises: 74585af9596b, 87b208812990
Create Date: 2026-01-16 19:30:37.900560

"""
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = '90aa0fb7e9ad'
down_revision = ('74585af9596b', '87b208812990')
branch_labels = None
depends_on = None


def upgrade() -> None:
pass


def downgrade() -> None:
pass

Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"""Add trusted_member_request_reason field to users table

Revision ID: de4074ff4ff8
Revises: 90aa0fb7e9ad
Create Date: 2026-01-16 20:00:00.000000

"""
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = 'de4074ff4ff8'
down_revision = '90aa0fb7e9ad'
branch_labels = None
depends_on = None


def upgrade() -> None:
# Add trusted_member_request_reason column to users table
# This field stores the reason provided by users when requesting trusted member status
# It is required when user has less than 300 edits
# Superadmins can view this reason when reviewing requests
# Check if column exists before adding to handle partial migration scenarios
conn = op.get_bind()
inspector = sa.inspect(conn)
columns = [col['name'] for col in inspector.get_columns('users')]

if 'trusted_member_request_reason' not in columns:
op.add_column('users', sa.Column('trusted_member_request_reason', sa.Text(), nullable=True))


def downgrade() -> None:
# Remove trusted_member_request_reason column from users table
conn = op.get_bind()
inspector = sa.inspect(conn)
columns = [col['name'] for col in inspector.get_columns('users')]

if 'trusted_member_request_reason' in columns:
op.drop_column('users', 'trusted_member_request_reason')
14 changes: 11 additions & 3 deletions backend/app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,8 +248,9 @@ def check_cookie():
# --- Query User from Database ---
# CRITICAL: Query directly from database using raw SQL to bypass ALL ORM caching
# This ensures we get the absolute latest role from the database
# Include is_trusted_member to check if user can create contests
direct_query = db.session.execute(
sql_text('SELECT id, username, email, role FROM users WHERE id = :user_id'),
sql_text('SELECT id, username, email, role, is_trusted_member FROM users WHERE id = :user_id'),
{'user_id': int(user_id)}
).fetchone()

Expand All @@ -268,6 +269,7 @@ def check_cookie():
db_username = direct_query[1]
db_email = direct_query[2]
db_role = direct_query[3]
db_is_trusted_member = direct_query[4] if len(direct_query) > 4 else False

# Log what we got from the database - CRITICAL DEBUG INFO
try:
Expand All @@ -294,7 +296,7 @@ def check_cookie():
# --- Double-check by Username ---
# Also verify by username as a double-check (in case there's any ID mismatch)
username_verify = db.session.execute(
sql_text('SELECT id, username, email, role FROM users WHERE username = :username'),
sql_text('SELECT id, username, email, role, is_trusted_member FROM users WHERE username = :username'),
{'username': db_username}
).fetchone()

Expand All @@ -320,12 +322,18 @@ def check_cookie():
role_value = str(db_role).strip().lower() if db_role else 'user'

# Build response using data directly from database (no ORM objects)
# Include trusted member status for frontend permission checks
# Superadmins are automatically trusted, so check both role and is_trusted_member
is_trusted = bool(db_is_trusted_member) or role_value == 'superadmin'

response_data = {
'userId': db_user_id,
'username': db_username,
'email': db_email,
# Use role directly from database query - most reliable source
'role': role_value
'role': role_value,
# Include trusted member status so frontend can check if user can create contests
'is_trusted_member': is_trusted
}

# Log the final response being sent - CRITICAL DEBUG INFO
Expand Down
Loading