2# +==== BEGIN CatFeeder =================+
5# ...............)..(.')
7# ...............\(__)|
8# Inspired by Joan Stark
9# source https://www.asciiart.eu/
14# CREATION DATE: 11-10-2025
15# LAST Modified: 21:32:4 01-02-2026
17# This is the backend server in charge of making the actual website work.
19# COPYRIGHT: (c) Cat Feeder
20# PURPOSE: This is the the class in charge of containing the non-http boilerplates.
22# +==== END CatFeeder =================+
25from typing
import TYPE_CHECKING, Union, List, Optional, Dict, Any
28from random
import randint
29from datetime
import datetime, timedelta
30from fastapi
import Response
32from display_tty
import Disp, initialise_logger
34from ..core
import FinalSingleton
35from ..core.runtime_manager
import RuntimeManager, RI
36from ..utils
import constants
as CONST
37from ..sql.sql_manager
import SQL
40 from .responses
import BoilerplateResponses
47 disp: Disp = initialise_logger(__qualname__,
False)
49 def __init__(self, success: int = 0, error: int = 84, debug: bool =
False) ->
None:
50 """Initialize the BoilerplateNonHTTP instance.
51 The initializer configures the logger, stores commonly used status codes and
52 runtime manager references, and obtains shared instances such as the
53 database link and optional response helper.
56 success (int): Numeric code representing a successful operation. Defaults to 0.
57 error (int): Numeric code representing an error. Defaults to 84.
58 debug (bool): Enable debug logging when True. Defaults to False.
61 self.
disp.update_disp_debug(debug)
62 self.
disp.log_debug(
"Initialising...")
71 "BoilerplateResponses",
None)
72 self.
disp.log_debug(
"Initialised")
76 This is a pause function that works in the same wat as the batch pause command.
77 It pauses the program execution until the user presses the enter key.
80 str: _description_: The input from the user
82 return input(
"Press enter to continue...")
86 The function to set the lifespan of the user token
88 seconds (int): Seconds
91 datetime: The datetime of the lifespan of the token
93 current_time = datetime.now()
94 offset_time = current_time + timedelta(seconds=seconds)
98 """Check if a given token correspond to a user that is an administrator or not.
101 token (str): The token to analyse.
104 bool: The administrative status.
106 title: str =
"is_token_admin"
110 if not isinstance(usr_id, str):
112 current_user_raw: Union[int, List[Dict[str, Any]]] = self.
database_link.get_data_from_table(
113 table=CONST.TAB_ACCOUNTS,
115 where=f
"id='{usr_id}'",
118 self.
disp.log_debug(f
"Queried data = {current_user_raw}")
119 if isinstance(current_user_raw, int)
or current_user_raw == []:
121 if "admin" in current_user_raw[0]:
122 if str(current_user_raw[0].get(
"admin",
"0")) ==
"1":
123 self.
disp.log_warning(
124 f
"User account {usr_id} with name {current_user_raw[0].get('username', '<unknown_username>')} is an admin"
126 self.
disp.log_warning(
127 "They probably called an admin endpoint."
133 """Refresh the expiration date for a connection token.
134 The function computes a new expiration datetime using :meth:`set_lifespan`
135 and persists it to the database using the configured SQL manager. The
136 stored value is formatted for SQL using :meth:`SQL.datetime_to_string`.
139 token (str): The connection token whose expiration should be updated.
142 int: The status code returned by the database update operation. This will be `self.success` on success or an error code on failure.
144 self.
disp.log_debug(
"The token is still valid, updating lifespan.")
146 CONST.UA_TOKEN_LIFESPAN
148 self.
disp.log_debug(f
"New token lifespan: {new_date}")
150 datetime_instance=new_date,
154 self.
disp.log_debug(f
"string date: {new_date_str}")
156 table=CONST.TAB_CONNECTIONS,
158 column=[
"expiration_date"],
159 where=f
"token='{token}'"
162 self.
disp.log_debug(
"Token expiration date updated.")
164 self.
disp.log_error(
"Failed to update token lifespan.")
169 Check if the token is correct.
171 token (str): _description_: The token to check
174 bool: _description_: True if the token is correct, False otherwise
176 title =
"is_token_correct"
177 self.
disp.log_debug(
"Checking if the token is correct.", title)
178 if isinstance(token, str)
is False:
181 CONST.TAB_CONNECTIONS,
183 where=f
"token='{token}'",
186 if isinstance(login_table, int):
188 if len(login_table) != 1:
190 self.
disp.log_debug(f
"login_table = {login_table}", title)
191 if datetime.now() > login_table[0][0]:
192 self.
disp.log_warning(
193 "The provided token is invalid due to excessive idle time."
196 self.
disp.log_debug(
"The token is still valid, updating lifespan.")
199 self.
disp.log_warning(
200 f
"Failed to update expiration_date for {token}.",
207 This is a function that will generate a token for the user.
209 str: _description_: The token generated
211 title =
"generate_token"
212 token = str(uuid.uuid4())
214 table=CONST.TAB_CONNECTIONS,
216 where=f
"token='{token}'",
219 if isinstance(user_token, int):
221 if isinstance(user_token, list)
and token
not in user_token:
223 self.
disp.log_debug(f
"user_token = {user_token}", title)
224 while not isinstance(user_token, int):
225 token = str(uuid.uuid4())
227 table=CONST.TAB_CONNECTIONS,
229 where=f
"token='{token}'",
234 self.
disp.log_debug(f
"user_token = {user_token}", title)
235 if isinstance(user_token, int)
and user_token == self.
error:
237 if isinstance(user_token, list)
and token
not in user_token:
241 def server_show_item_content(self, function_name: str =
"show_item_content", item_name: str =
"", item: object =
None, show: bool =
True) ->
None:
243 This is a function that will display the content of an item.
244 The purpose of this function is more for debugging purposes.
246 function_name (str, optional): _description_. Defaults to "show_item_content".
247 item (object, optional): _description_. Defaults to None.
252 f
"({function_name}) dir({item_name}) = {dir(item)}",
253 "pet_server_show_item_content"
256 if i
in (
"auth",
"session",
"user"):
258 f
"({function_name}) skipping {item_name}.{i}"
262 f
"({function_name}) {item_name}.{i} = {getattr(item, i)}"
267 This is a function that will check if the date is correct or not.
269 date (str, optional): _description_: The date to check. Defaults to "DD/MM/YYYY".
272 bool: _description_: True if the date is correct, False otherwise
275 pattern = re.compile(
276 r"^(0[1-9]|[12][0-9]|3[01])/(0[1-9]|1[0-2])/\d{4}$"
278 if not pattern.match(date):
282 datetime.strptime(date,
"%d/%m/%Y")
289 Create a token that can be used for e-mail verification.
294 if isinstance(token_size, (int, float))
is False:
296 token_size = int(token_size)
297 token_size = max(token_size, 0)
298 code = f
"{randint(CONST.RANDOM_MIN, CONST.RANDOM_MAX)}"
299 for i
in range(token_size):
300 code += f
"-{randint(CONST.RANDOM_MIN, CONST.RANDOM_MAX)}"
303 def update_single_data(self, table: str, column_finder: str, column_to_update: str, data_finder: str, request_body: dict) -> int:
305 The function in charge of updating the data in the database
309 [request_body[column_to_update]],
311 f
"{column_finder}='{data_finder}'"
318 The function in charge of getting the user id based of the provided content.
321 title (str): _description_: The title of the endpoint calling it
322 token (str): _description_: The token of the user account
325 Optional[Union[str, Response]]: _description_: Returns as string id if success, otherwise, a pre-made response for the endpoint.
327 function_title =
"get_user_id_from_token"
328 usr_id_node: str =
"user_id"
333 "BoilerplateResponses not found, retuning None",
334 f
"{title}:{function_title}"
338 f
"Getting user id based on {token}", function_title
340 current_user_raw: Union[int, List[Dict[str, Any]]] = self.
database_link.get_data_from_table(
341 table=CONST.TAB_CONNECTIONS,
343 where=f
"token='{token}'",
346 if isinstance(current_user_raw, int):
348 current_user: List[Dict[str, Any]] = current_user_raw
349 self.
disp.log_debug(f
"current_user = {current_user}", function_title)
350 if current_user == self.
error:
353 f
"user_length = {len(current_user)}", function_title
355 if len(current_user) == 0
or len(current_user) > 1:
358 f
"current_user[0] = {current_user[0]}", function_title
360 if usr_id_node
not in current_user[0]:
362 msg =
"str(current_user[0]["
363 msg += f
"{usr_id_node}]) = {str(current_user[0][usr_id_node])}"
364 self.
disp.log_debug(msg, function_title)
365 return str(current_user[0][usr_id_node])
367 def update_user_data(self, title: str, usr_id: str, line_content: List[Optional[Union[str, int, float]]]) -> Optional[Union[int, Response]]:
369 Update the account information based on the provided line.
372 title (str): _description_: This is the title of the endpoint
373 usr_id (str): _description_: This is the id of the user that needs to be updated
374 line_content (List[str]): _description_: The content of the line to be edited.
377 Union[int, Response]: _description_
383 self.
disp.log_debug(f
"Compile line_content: {line_content}.", title)
384 columns_raw: Union[int, List[str]] = self.
database_link.get_table_column_names(
387 if isinstance(columns_raw, int):
389 columns: List[str] = columns_raw
390 self.
disp.log_debug(f
"Removing id from columns: {columns}.", title)
393 table=CONST.TAB_ACCOUNTS,
396 where=f
"id='{usr_id}'"
398 if status == self.
error:
404 Remove the user from the provided tables.
407 where (str): _description_: The id of the user to remove
408 tables (List[str]): _description_: The tables to remove the user from
411 int: _description_: The status of the operation
413 title =
"remove_user_from_tables"
414 if not isinstance(tables, (List, tuple, str)):
416 f
"Expected tables to be of type list but got {type(tables)}",
420 if isinstance(tables, str):
421 self.
disp.log_warning(
422 "Tables is of type str, converting to list[str].", title
431 deletion_status[str(table)] = status
432 if status == self.
error:
433 self.
disp.log_warning(
434 f
"Failed to remove data from table: {table}",
437 return deletion_status
441 Hide the api key from the user.
444 api_key (str): _description_: The api key to hide
447 str: _description_: The hidden api key
449 title =
"hide_api_key"
450 self.
disp.log_debug(f
"api_key = {api_key}", title)
452 api_key =
"No api key"
454 api_key =
"Some api key"
455 self.
disp.log_debug(f
"api_key after: {api_key}", title)
int update_lifespan(self, str token)
Optional[Union[str, Response]] get_user_id_from_token(self, str title, str token)
str hide_api_key(self, str api_key)
None __init__(self, int success=0, int error=84, bool debug=False)
RuntimeManager runtime_manager
str generate_check_token(self, int token_size=4)
int update_single_data(self, str table, str column_finder, str column_to_update, str data_finder, dict request_body)
Optional[BoilerplateResponses] boilerplate_responses
Union[int, Dict[str, int]] remove_user_from_tables(self, str where, List[str] tables)
bool check_date(self, str date="DD/MM/YYYY")
bool is_token_correct(self, str token)
Optional[Union[int, Response]] update_user_data(self, str title, str usr_id, List[Optional[Union[str, int, float]]] line_content)
bool is_token_admin(self, str token)
datetime set_lifespan(self, int seconds)
None server_show_item_content(self, str function_name="show_item_content", str item_name="", object item=None, bool show=True)