Cat Feeder  1.0.0
The Cat feeder project
Loading...
Searching...
No Matches
constants.py
Go to the documentation of this file.
1r"""
2# +==== BEGIN CatFeeder =================+
3# LOGO:
4# ..............(..../\
5# ...............)..(.')
6# ..............(../..)
7# ...............\‍(__)|
8# Inspired by Joan Stark
9# source https://www.asciiart.eu/
10# animals/cats
11# /STOP
12# PROJECT: CatFeeder
13# FILE: constants.py
14# CREATION DATE: 11-10-2025
15# LAST Modified: 19:0:27 31-01-2026
16# DESCRIPTION:
17# This is the backend server in charge of making the actual website work.
18# Backend server constants and configuration management.
19#
20# This module contains all the configuration constants and utility functions
21# required to run the CatFeeder server. It manages environment variables,
22# TOML configurations, database settings, server configurations, and provides
23# helper functions for data manipulation.
24#
25# Module-level constants include:
26# - Database connection parameters (host, port, user, password, database)
27# - Server configurations (workers, timeout, development/production settings)
28# - Verification settings (email delays, token sizes)
29# - Table names and database schema constants
30# - JSON response key constants
31# - User data field mappings and indices
32# /STOP
33# COPYRIGHT: (c) Cat Feeder
34# PURPOSE: This is the file in charge of containing the constants that run the server.
35# // AR
36# +==== END CatFeeder =================+
37"""
38
39import re
40from typing import List, Tuple, Any, Dict
41from pathlib import Path
42
43from display_tty import Disp, initialise_logger
44
45from ..config import TOMLLoader, EnvLoader, refresh_debug
46
47IDISP: Disp = initialise_logger("Constants", False)
48
49
50# Environement initialisation
51ENV: EnvLoader = EnvLoader()
52
53# toml config file
54TOML: TOMLLoader = TOMLLoader()
55
56# Base directory
57ROOT_DIRECTORY: Path = Path(__file__).parent.parent.parent.parent
58
59# Assets directory
60ASSETS_DIRECTORY: Path = ROOT_DIRECTORY / "assets"
61
62# Enable debugging for the functions in the constants file.
63DEBUG: bool = refresh_debug(False)
64
65# Server oauth variables
66REDIRECT_URI = ENV.get_environment_variable("REDIRECT_URI")
67
68# Database management
69DB_HOST = ENV.get_environment_variable("DB_HOST")
70DB_PORT_RAW = ENV.get_environment_variable("DB_PORT")
71if not DB_PORT_RAW.isdigit():
72 raise TypeError("Expected a number for the database port")
73DB_PORT = int(DB_PORT_RAW)
74DB_USER = ENV.get_environment_variable("DB_USER")
75DB_PASSWORD = ENV.get_environment_variable("DB_PASSWORD")
76DB_DATABASE = ENV.get_environment_variable("DB_DATABASE")
77
78# TOML variables
79# |- Server configurations
80SERVER_WORKERS = TOML.get_toml_variable(
81 "Server_configuration", "workers", None
82)
83SERVER_LIFESPAN = TOML.get_toml_variable(
84 "Server_configuration", "lifespan", "auto"
85)
86SERVER_TIMEOUT_KEEP_ALIVE = TOML.get_toml_variable(
87 "Server_configuration", "timeout_keep_alive", 30
88)
89
90# |- Server configuration -> Status codes
91SUCCESS = int(TOML.get_toml_variable(
92 "Server_configuration.status_codes", "success", 0
93))
94ERROR = int(TOML.get_toml_variable(
95 "Server_configuration.status_codes", "error", -84
96))
97
98
99# |- Server configuration -> development
100SERVER_DEV_RELOAD = TOML.get_toml_variable(
101 "Server_configuration.development", "reload", False
102)
103SERVER_DEV_RELOAD_DIRS = TOML.get_toml_variable(
104 "Server_configuration.development", "reload_dirs", None
105)
106SERVER_DEV_LOG_LEVEL = TOML.get_toml_variable(
107 "Server_configuration.development", "log_level", "info"
108)
109SERVER_DEV_USE_COLOURS = TOML.get_toml_variable(
110 "Server_configuration.development", "use_colours", True
111)
112
113# |- Server configuration -> production
114SERVER_PROD_PROXY_HEADERS = TOML.get_toml_variable(
115 "Server_configuration.production", "proxy_headers", True
116)
117SERVER_PROD_FORWARDED_ALLOW_IPS = TOML.get_toml_variable(
118 "Server_configuration.production", "forwarded_allow_ips", None
119)
120
121# |- Server configuration -> database settings
122DATABASE_POOL_NAME = TOML.get_toml_variable(
123 "Server_configuration.database", "pool_name", None
124)
125DATABASE_MAX_POOL_CONNECTIONS = int(TOML.get_toml_variable(
126 "Server_configuration.database", "max_pool_connections", 10
127))
128DATABASE_RESET_POOL_NODE_CONNECTION = TOML.get_toml_variable(
129 "Server_configuration.database", "reset_pool_node_connection", True
130)
131DATABASE_CONNECTION_TIMEOUT = int(TOML.get_toml_variable(
132 "Server_configuration.database", "connection_timeout", 10
133))
134DATABASE_LOCAL_INFILE = TOML.get_toml_variable(
135 "Server_configuration.database", "local_infile", False
136)
137DATABASE_INIT_COMMAND = TOML.get_toml_variable(
138 "Server_configuration.database", "init_command", None
139)
140DATABASE_DEFAULT_FILE = TOML.get_toml_variable(
141 "Server_configuration.database", "default_file", None
142)
143DATABASE_SSL_KEY = TOML.get_toml_variable(
144 "Server_configuration.database", "ssl_key", None
145)
146DATABASE_SSL_CERT = TOML.get_toml_variable(
147 "Server_configuration.database", "ssl_cert", None
148)
149DATABASE_SSL_CA = TOML.get_toml_variable(
150 "Server_configuration.database", "ssl_ca", None
151)
152DATABASE_SSL_CIPHER = TOML.get_toml_variable(
153 "Server_configuration.database", "ssl_cipher", None
154)
155DATABASE_SSL_VERIFY_CERT = TOML.get_toml_variable(
156 "Server_configuration.database", "ssl_verify_cert", False
157)
158DATABASE_SSL = TOML.get_toml_variable(
159 "Server_configuration.database", "ssl", None
160)
161DATABASE_AUTOCOMMIT = TOML.get_toml_variable(
162 "Server_configuration.database", "autocommit", False
163)
164DATABASE_COLLATION = TOML.get_toml_variable(
165 "Server_configuration.database", "collation", "utf8mb4_unicode_ci"
166)
167
168# |- Verification
169EMAIL_VERIFICATION_DELAY = int(TOML.get_toml_variable(
170 "Verification", "email_verification_delay", 120
171))
172CHECK_TOKEN_SIZE = int(TOML.get_toml_variable(
173 "Verification", "check_token_size", 4
174))
175RANDOM_MIN = int(TOML.get_toml_variable(
176 "Verification", "random_min", 100000
177))
178RANDOM_MAX = int(TOML.get_toml_variable(
179 "Verification", "random_max", 999999
180))
181
182# |- Services
183API_REQUEST_DELAY = int(TOML.get_toml_variable(
184 "Services", "api_request_delay", 5
185))
186
187# |- Front-end assets
188FRONT_END_ASSETS_REFRESH: int = int(TOML.get_toml_variable(
189 "Frontend", "asset_cache_refresh_interval", 300
190))
191
192# Json default keys
193JSON_TITLE: str = "title"
194JSON_MESSAGE: str = "msg"
195JSON_ERROR: str = "error"
196JSON_RESP: str = "resp"
197JSON_LOGGED_IN: str = "logged in"
198JSON_UID: str = "user_uid"
199
200# Database table names
201TAB_PET = "Pet"
202TAB_ACCOUNTS = "Users"
203TAB_ACTIONS = "Actions"
204TAB_SERVICES = "Services"
205TAB_CONNECTIONS = "Connections"
206TAB_VERIFICATION = "Verification"
207TAB_ACTIVE_OAUTHS = "ActiveOauths"
208TAB_ACTION_LOGGING = "ActionLoging"
209TAB_ACTION_TEMPLATE = "ActionTemplate"
210TAB_USER_OAUTH_CONNECTION = "UserOauthConnection"
211
212# Character info config
213CHAR_NODE_KEY: str = "node"
214CHAR_ACTIVE_KEY: str = "active"
215CHAR_NAME_KEY: str = "name"
216CHAR_UID_KEY: str = "uid"
217CHAR_ID_DEFAULT_INDEX: int = 0
218
219
220# User info database table
221USERNAME_INDEX_DB: int = 1
222PASSWORD_INDEX_DB: int = 2
223FIRSTNAME_INDEX_DB: int = 3
224LASTNAME_INDEX_DB: int = 4
225BIRTHDAY_INDEX_DB: int = 5
226GENDER_INDEX_DB: int = 7
227ROLE_INDEX_DB: int = 10
228UD_USERNAME_KEY: str = "username"
229UD_FIRSTNAME_KEY: str = "firstname"
230UD_LASTNAME_KEY: str = "lastname"
231UD_BIRTHDAY_KEY: str = "birthday"
232UD_GENDER_KEY: str = "gender"
233UD_ROLE_KEY: str = "role"
234UD_ADMIN_KEY: str = "admin"
235UD_LOGIN_TIME_KEY: str = "login_time"
236UD_LOGGED_IN_KEY: str = "logged_in"
237
238# Incoming header variables
239REQUEST_TOKEN_KEY = "token"
240REQUEST_BEARER_KEY = "authorization"
241
242# Cache loop
243THREAD_CACHE_REFRESH_DELAY = 10
244
245# User sql data
246UA_TOKEN_LIFESPAN: int = 7200
247UA_EMAIL_KEY: str = "email"
248UA_LIFESPAN_KEY: str = "lifespan"
249
250# Get user info banned columns (filtered out columns)
251USER_INFO_BANNED: List[str] = ["password", "method", "favicon"]
252USER_INFO_ADMIN_NODE: str = "admin"
253
254# The path to the server icon
255ICON_PATH: str = str(
256 ASSETS_DIRECTORY / "icon" / "cat_feeder" / "favicon.ico"
257)
258
259# Path to the png version of the icon
260PNG_ICON_PATH: str = str(
261 ASSETS_DIRECTORY / "icon" / "cat_feeder" / "logo_256x256.png"
262)
263
264# Columns to ignore
265TABLE_COLUMNS_TO_IGNORE: Tuple[str, ...] = ("id", "creation_date", "edit_date")
266
267TABLE_COLUMNS_TO_IGNORE_USER: Tuple[str, ...] = (
268 "id", "creation_date", "edit_date", "last_connection", "deletion_date"
269)
270
271# Bucket info
272BUCKET_NAME: str = ENV.get_environment_variable("BUCKET_NAME")
273
274# The front-end assets directories
275STYLE_DIRECTORY: Path = ASSETS_DIRECTORY / "css"
276HTML_DIRECTORY: Path = ASSETS_DIRECTORY / "html"
277JS_DIRECTORY: Path = ASSETS_DIRECTORY / "js" / "web"
278IMG_DIRECTORY: Path = ASSETS_DIRECTORY / "icon" / "img"
279
280try:
281 GOOGLE_SITE_VERIFICATION_CODE: str = ENV.get_environment_variable(
282 "GOOGLE_SITE_VERIFICATION_CODE"
283 )
284except ValueError:
285 GOOGLE_SITE_VERIFICATION_CODE = ""
286
287
288def clean_list(raw_input: List[Any], items: Tuple[Any, ...], disp: Disp) -> List[Any]:
289 """Remove specified items from a list if they are present.
290
291 Iterates through the input list and removes all occurrences of items
292 specified in the items tuple. Logs debug information for each removal.
293
294 Args:
295 raw_input (List[Any]): The list to check and modify.
296 items (Tuple[Any, Any]): The items to remove from the list.
297 disp (Disp): The logging object for debug output.
298
299 Returns:
300 List[Any]: The modified list with specified items removed.
301 """
302 to_pop = []
303 disp.log_debug(f"initial list: {raw_input}")
304 for index, item in enumerate(raw_input):
305 if item in items:
306 to_pop.append(index)
307 disp.log_debug(f"index to pop: {index}, item: {item}")
308 max_length = len(to_pop)
309 while max_length > 0:
310 node = to_pop[max_length-1]
311 node_value = raw_input.pop(node)
312 disp.log_debug(f"Popped item[{max_length-1}] = {node} -> {node_value}")
313 max_length -= 1
314 disp.log_debug(f"final list: {raw_input}")
315 return raw_input
316
317
318def clean_dict(raw_input: Dict[str, Any], items: Tuple[Any, ...], disp: Disp) -> Dict[str, Any]:
319 """Remove specified keys from a dictionary if they are present.
320
321 Iterates through the input dictionary and removes all keys specified
322 in the items tuple. Logs debug information for each removal.
323
324 Args:
325 input (Dict[str, Any]): The dictionary to check and modify.
326 items (Tuple[Any, Any]): The keys to remove from the dictionary.
327 disp (Disp): The logging object for debug output.
328
329 Returns:
330 Dict[str, Any]: The modified dictionary with specified keys removed.
331 """
332 for item in items:
333 if item in raw_input:
334 disp.log_debug(f"key to pop: {item}, value: {raw_input[item]}")
335 raw_input.pop(item)
336 disp.log_debug(f"Popped key: {item}")
337 disp.log_debug(f"final dictionary: {raw_input}")
338 return raw_input
339
340
341def mask_email_segment(segment: str) -> str:
342 """Mask a single email segment, showing first and last character."""
343 if len(segment) <= 2:
344 if segment:
345 return "[...]"
346 return ""
347 return f"{segment[0]}[...]{segment[-1]}"
348
349
350def hide_user_email(user_email: str, disp: Disp) -> str:
351 """Mask user email for privacy while preserving structure and shape.
352
353 Masks each word/segment separately, showing only first and last character.
354 Segments are separated by special characters: . + - @
355
356 Args:
357 user_email (str): Email address to mask.
358
359 Returns:
360 str: Masked email (e.g., t[...]t.m[...]e+r[...]m@g[...]l.c[...]m).
361 """
362
363 if "" == user_email:
364 disp.log_warning("Got empty email string to mask")
365 return "<unknown_email>"
366
367 if "@" not in user_email:
368 disp.log_warning(f"Got invalid email format: '{user_email}'")
369 return "<invalid_email>"
370
371 disp.log_debug(f"Masking email: {user_email}")
372
373 # Split on special characters but keep them
374 segments = re.split(r"([.+\-@])", user_email)
375 disp.log_debug(f"Split segments: {segments}")
376
377 # Process each segment
378 masked_parts = []
379 for segment in segments:
380 if not segment:
381 # Skip empty segments
382 continue
383
384 # Check if segment is a special character
385 is_special_char = re.match(r"[.+\-@]", segment)
386
387 if is_special_char:
388 # Keep special characters as-is
389 disp.log_debug(f"Keeping special character: '{segment}'")
390 masked_parts.append(segment)
391 else:
392 # Mask alphanumeric segments
393 masked_segment = mask_email_segment(segment)
394 disp.log_debug(f"Masked segment '{segment}' -> '{masked_segment}'")
395 masked_parts.append(masked_segment)
396
397 # Join all parts back together
398 result = "".join(masked_parts)
399 disp.log_debug(f"Final masked email: {result}")
400 return result
List[Any] clean_list(List[Any] raw_input, Tuple[Any,...] items, Disp disp)
Definition constants.py:288
str mask_email_segment(str segment)
Definition constants.py:341
Dict[str, Any] clean_dict(Dict[str, Any] raw_input, Tuple[Any,...] items, Disp disp)
Definition constants.py:318
str hide_user_email(str user_email, Disp disp)
Definition constants.py:350