Update app.py

This commit is contained in:
admin 2026-10-08 00:43:17 +00:00
parent 33eac1c48d
commit 1efe0817b2

98
app.py
View File

@ -59,6 +59,7 @@ MAX_LOCATION_LENGTH = 120
MAX_WEBSITE_LENGTH = 300 MAX_WEBSITE_LENGTH = 300
MAX_CUSTOM_CSS_LENGTH = 20000 MAX_CUSTOM_CSS_LENGTH = 20000
MAX_PROFILE_BADGE_LENGTH = 48 MAX_PROFILE_BADGE_LENGTH = 48
MAX_STATUS_MESSAGE_LENGTH = 160
MAX_OWNED_TEAMS_PER_USER = 3 MAX_OWNED_TEAMS_PER_USER = 3
MAX_MESSAGE_EDIT_WINDOW_SECONDS = 15 * 60 MAX_MESSAGE_EDIT_WINDOW_SECONDS = 15 * 60
MAX_SEARCH_QUERY_LENGTH = 120 MAX_SEARCH_QUERY_LENGTH = 120
@ -850,7 +851,7 @@ def csrf_valid() -> bool:
return bool(supplied and expected and hmac.compare_digest(supplied, expected)) return bool(supplied and expected and hmac.compare_digest(supplied, expected))
def user_payload(row: sqlite3.Row) -> dict: def user_payload(row: sqlite3.Row, settings_row: Optional[sqlite3.Row] = None) -> dict:
status = row["status"] status = row["status"]
restricted_until = parse_iso(row["restricted_until"]) restricted_until = parse_iso(row["restricted_until"])
if status == "restricted" and restricted_until and restricted_until <= utc_now(): if status == "restricted" and restricted_until and restricted_until <= utc_now():
@ -861,6 +862,7 @@ def user_payload(row: sqlite3.Row) -> dict:
"role": row["role"], "role": row["role"],
"status": status, "status": status,
"restricted_until": row["restricted_until"], "restricted_until": row["restricted_until"],
"presence_mode": settings_row["presence_mode"] if settings_row and "presence_mode" in settings_row.keys() else "online",
} }
@ -882,11 +884,19 @@ def current_user() -> Optional[dict]:
""", """,
(user_id,), (user_id,),
).fetchone() ).fetchone()
settings_row = db.execute(
"""
SELECT presence_mode
FROM user_settings
WHERE user_id = ?
""",
(user_id,),
).fetchone()
if not row: if not row:
session.clear() session.clear()
return None return None
user = user_payload(row) user = user_payload(row, settings_row)
if user["status"] == "restricted": if user["status"] == "restricted":
expires = parse_iso(user["restricted_until"]) expires = parse_iso(user["restricted_until"])
if expires and expires <= utc_now(): if expires and expires <= utc_now():
@ -922,6 +932,7 @@ def login_user(row: sqlite3.Row | dict) -> dict:
"role": row["role"], "role": row["role"],
"status": row["status"], "status": row["status"],
"restricted_until": row.get("restricted_until") if isinstance(row, dict) else row["restricted_until"], "restricted_until": row.get("restricted_until") if isinstance(row, dict) else row["restricted_until"],
"presence_mode": row.get("presence_mode", "online") if isinstance(row, dict) else "online",
} }
@ -1376,8 +1387,11 @@ def init_db() -> None:
bio_mode TEXT NOT NULL DEFAULT 'txt', bio_mode TEXT NOT NULL DEFAULT 'txt',
website TEXT, website TEXT,
location TEXT, location TEXT,
status_message TEXT,
banner_attachment_id INTEGER,
updated_at TEXT NOT NULL, updated_at TEXT NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (banner_attachment_id) REFERENCES attachments(id) ON DELETE SET NULL
) )
""" """
) )
@ -1389,6 +1403,11 @@ def init_db() -> None:
name_color TEXT, name_color TEXT,
custom_css TEXT NOT NULL DEFAULT '', custom_css TEXT NOT NULL DEFAULT '',
theme_mode TEXT NOT NULL DEFAULT 'dark', theme_mode TEXT NOT NULL DEFAULT 'dark',
profile_background_color TEXT,
profile_banner_color TEXT,
profile_visibility TEXT NOT NULL DEFAULT 'public',
last_active_visibility TEXT NOT NULL DEFAULT 'everyone',
presence_mode TEXT NOT NULL DEFAULT 'online',
updated_at TEXT NOT NULL, updated_at TEXT NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
) )
@ -1610,10 +1629,24 @@ def init_db() -> None:
db.execute("ALTER TABLE messages ADD COLUMN direct_conversation_id INTEGER") db.execute("ALTER TABLE messages ADD COLUMN direct_conversation_id INTEGER")
if not column_exists(db, "user_settings", "name_color"): if not column_exists(db, "user_settings", "name_color"):
db.execute("ALTER TABLE user_settings ADD COLUMN name_color TEXT") db.execute("ALTER TABLE user_settings ADD COLUMN name_color TEXT")
if not column_exists(db, "user_settings", "profile_background_color"):
db.execute("ALTER TABLE user_settings ADD COLUMN profile_background_color TEXT")
if not column_exists(db, "user_settings", "profile_banner_color"):
db.execute("ALTER TABLE user_settings ADD COLUMN profile_banner_color TEXT")
if not column_exists(db, "user_settings", "profile_visibility"):
db.execute("ALTER TABLE user_settings ADD COLUMN profile_visibility TEXT NOT NULL DEFAULT 'public'")
if not column_exists(db, "user_settings", "last_active_visibility"):
db.execute("ALTER TABLE user_settings ADD COLUMN last_active_visibility TEXT NOT NULL DEFAULT 'everyone'")
if not column_exists(db, "user_settings", "presence_mode"):
db.execute("ALTER TABLE user_settings ADD COLUMN presence_mode TEXT NOT NULL DEFAULT 'online'")
if not column_exists(db, "presence_events", "direct_conversation_id"): if not column_exists(db, "presence_events", "direct_conversation_id"):
db.execute("ALTER TABLE presence_events ADD COLUMN direct_conversation_id INTEGER") db.execute("ALTER TABLE presence_events ADD COLUMN direct_conversation_id INTEGER")
if not column_exists(db, "user_profiles", "badge_text"): if not column_exists(db, "user_profiles", "badge_text"):
db.execute("ALTER TABLE user_profiles ADD COLUMN badge_text TEXT") db.execute("ALTER TABLE user_profiles ADD COLUMN badge_text TEXT")
if not column_exists(db, "user_profiles", "status_message"):
db.execute("ALTER TABLE user_profiles ADD COLUMN status_message TEXT")
if not column_exists(db, "user_profiles", "banner_attachment_id"):
db.execute("ALTER TABLE user_profiles ADD COLUMN banner_attachment_id INTEGER")
if not column_exists(db, "pages", "parent_page_id"): if not column_exists(db, "pages", "parent_page_id"):
db.execute("ALTER TABLE pages ADD COLUMN parent_page_id INTEGER") db.execute("ALTER TABLE pages ADD COLUMN parent_page_id INTEGER")
@ -1810,6 +1843,8 @@ def init_db() -> None:
bio_mode, bio_mode,
website, website,
location, location,
status_message,
banner_attachment_id,
updated_at updated_at
) )
SELECT SELECT
@ -1820,6 +1855,8 @@ def init_db() -> None:
'txt', 'txt',
'', '',
'', '',
'',
NULL,
? ?
FROM users FROM users
""", """,
@ -1833,6 +1870,11 @@ def init_db() -> None:
name_color, name_color,
custom_css, custom_css,
theme_mode, theme_mode,
profile_background_color,
profile_banner_color,
profile_visibility,
last_active_visibility,
presence_mode,
updated_at updated_at
) )
SELECT SELECT
@ -1841,6 +1883,11 @@ def init_db() -> None:
NULL, NULL,
'', '',
'dark', 'dark',
NULL,
NULL,
'public',
'everyone',
'online',
? ?
FROM users FROM users
""", """,
@ -1927,8 +1974,10 @@ def bootstrap_admin() -> None:
bio_mode, bio_mode,
website, website,
location, location,
status_message,
banner_attachment_id,
updated_at updated_at
) VALUES (?, ?, '', '', 'txt', '', '', ?) ) VALUES (?, ?, '', '', 'txt', '', '', '', NULL, ?)
""", """,
(user_id, username, now_iso()), (user_id, username, now_iso()),
) )
@ -1940,8 +1989,13 @@ def bootstrap_admin() -> None:
name_color, name_color,
custom_css, custom_css,
theme_mode, theme_mode,
profile_background_color,
profile_banner_color,
profile_visibility,
last_active_visibility,
presence_mode,
updated_at updated_at
) VALUES (?, '#f7dce8', NULL, '', 'dark', ?) ) VALUES (?, '#f7dce8', NULL, '', 'dark', NULL, NULL, 'public', 'everyone', 'online', ?)
""", """,
(user_id, now_iso()), (user_id, now_iso()),
) )
@ -2019,6 +2073,7 @@ def register():
"role": "user", "role": "user",
"status": "active", "status": "active",
"restricted_until": None, "restricted_until": None,
"presence_mode": "online",
} }
db_write( db_write(
""" """
@ -2030,8 +2085,10 @@ def register():
bio_mode, bio_mode,
website, website,
location, location,
status_message,
banner_attachment_id,
updated_at updated_at
) VALUES (?, ?, '', '', 'txt', '', '', ?) ) VALUES (?, ?, '', '', 'txt', '', '', '', NULL, ?)
""", """,
(user["id"], username, now_iso()), (user["id"], username, now_iso()),
) )
@ -2043,8 +2100,13 @@ def register():
name_color, name_color,
custom_css, custom_css,
theme_mode, theme_mode,
profile_background_color,
profile_banner_color,
profile_visibility,
last_active_visibility,
presence_mode,
updated_at updated_at
) VALUES (?, '#f7dce8', NULL, '', 'dark', ?) ) VALUES (?, '#f7dce8', NULL, '', 'dark', NULL, NULL, 'public', 'everyone', 'online', ?)
""", """,
(user["id"], now_iso()), (user["id"], now_iso()),
) )
@ -2084,14 +2146,17 @@ def login():
row = db.execute( row = db.execute(
""" """
SELECT SELECT
id, u.id,
username, u.username,
password_hash, u.password_hash,
role, u.role,
status, u.status,
restricted_until u.restricted_until,
FROM users us.presence_mode
WHERE username = ? FROM users u
LEFT JOIN user_settings us
ON us.user_id = u.id
WHERE u.username = ?
""", """,
(username,), (username,),
).fetchone() ).fetchone()
@ -2102,7 +2167,7 @@ def login():
return jsonify({"error": "Invalid username or password."}), 401 return jsonify({"error": "Invalid username or password."}), 401
db_write("UPDATE users SET last_login_at = ? WHERE id = ?", (now_iso(), row["id"])) db_write("UPDATE users SET last_login_at = ? WHERE id = ?", (now_iso(), row["id"]))
authenticated = login_user(row) authenticated = login_user(dict(row))
audit("account_logged_in", row["id"], "user", str(row["id"])) audit("account_logged_in", row["id"], "user", str(row["id"]))
return jsonify( return jsonify(
{ {
@ -2237,6 +2302,7 @@ register_routes(
"MAX_WEBSITE_LENGTH": MAX_WEBSITE_LENGTH, "MAX_WEBSITE_LENGTH": MAX_WEBSITE_LENGTH,
"MAX_CUSTOM_CSS_LENGTH": MAX_CUSTOM_CSS_LENGTH, "MAX_CUSTOM_CSS_LENGTH": MAX_CUSTOM_CSS_LENGTH,
"MAX_PROFILE_BADGE_LENGTH": MAX_PROFILE_BADGE_LENGTH, "MAX_PROFILE_BADGE_LENGTH": MAX_PROFILE_BADGE_LENGTH,
"MAX_STATUS_MESSAGE_LENGTH": MAX_STATUS_MESSAGE_LENGTH,
"MAX_OWNED_TEAMS_PER_USER": MAX_OWNED_TEAMS_PER_USER, "MAX_OWNED_TEAMS_PER_USER": MAX_OWNED_TEAMS_PER_USER,
"MAX_PAGE_COMMENT_LENGTH": MAX_PAGE_COMMENT_LENGTH, "MAX_PAGE_COMMENT_LENGTH": MAX_PAGE_COMMENT_LENGTH,
"ROOM_VISIBILITIES": ROOM_VISIBILITIES, "ROOM_VISIBILITIES": ROOM_VISIBILITIES,