Cat Feeder  1.0.0
The Cat feeder project
Loading...
Searching...
No Matches
incoming.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: incoming.py
14# CREATION DATE: 11-10-2025
15# LAST Modified: 22:47:49 14-01-2026
16# DESCRIPTION:
17# This is the backend server in charge of making the actual website work.
18# /STOP
19# COPYRIGHT: (c) Cat Feeder
20# PURPOSE: File containing boilerplate functions that could be used by the server in it's endpoints_initialised for checking incoming data.
21# // AR
22# +==== END CatFeeder =================+
23"""
24
25from typing import Union, Dict, Any, Optional
26from fastapi import Request, UploadFile
27from display_tty import Disp, initialise_logger
28
29from .non_web import BoilerplateNonHTTP
30from ..utils import constants as CONST
31from ..core import FinalSingleton
32from ..core.runtime_manager import RuntimeManager, RI
33from ..sql import SQL
34
35
37 """_summary_
38 """
39
40 disp: Disp = initialise_logger(__qualname__, False)
41
42 def __init__(self, error: int = 84, success: int = 0, debug: bool = False) -> None:
43 # ------------------------ The logging function ------------------------
44 self.disp.update_disp_debug(debug)
45 self.disp.log_debug("Initialising...")
46 # -------------------------- Inherited values --------------------------
47 self.debug: bool = debug
48 self.success: int = success
49 self.error: int = error
50 self.runtime_manager: RuntimeManager = RI
51 # -------------------------- Shared instances --------------------------
52 self.boilerplate_non_http_initialised: Optional[BoilerplateNonHTTP] = self.runtime_manager.get_if_exists(
53 BoilerplateNonHTTP,
54 None
55 )
56 self.database_link: SQL = self.runtime_manager.get(SQL)
57 self.disp.log_debug("Initialised")
58
59 def token_correct(self, request: Request) -> bool:
60 """_summary_
61 This is a function that will check if the token is correct or not.
62 Args:
63 request (Request): _description_: The request object
64
65 Returns:
66 bool: _description_: True if the token is correct, False otherwise
67 """
68 title = "token_correct"
69 self.disp.log_debug(
70 f"request = {request}", title
71 )
72 token = self.get_token_if_present(request)
73 self.disp.log_debug(
74 f"token = {token}", title
75 )
76 if token is None:
77 return False
78 self.boilerplate_non_http_initialised = self.runtime_manager.get_if_exists(
79 BoilerplateNonHTTP,
81 )
83 self.disp.log_error("BoilerplateNonHttp is missing")
84 raise RuntimeError("Token validation service unavailable")
85 return self.boilerplate_non_http_initialised.is_token_correct(token)
86
87 def logged_in(self, request: Request) -> bool:
88 """_summary_
89 This is a function that will check if the user is logged in or not.
90 Args:
91 request (Request): _description_: The request object
92
93 Returns:
94 bool: _description_: True if the user is logged in, False otherwise
95 """
96 title = "logged_in"
97 self.disp.log_debug(
98 f"request = {request}", title
99 )
100 self.disp.log_warning(
101 "This function is the same as token_correct, please call token correct instead",
102 title
103 )
104 return self.token_correct(request)
105
106 def _insert_login_into_database(self, user_data: dict[str, Any]) -> int:
107 """_summary_
108 Insert the user data into the database.
109 Args:
110 user_data (dict[str, any]): _description_: The user data to insert into the database
111
112 Returns:
113 int: _description_: The status of the operation
114 """
115 title = "_insert_login_into_database"
116 if len(user_data) != 3:
117 self.disp.log_error(
118 "The user data is not in the correct format !", title
119 )
120 return self.error
121 self.disp.log_debug(
122 f"user_data = {user_data}", title
123 )
124 user_data[-1] = self.database_link.datetime_to_string(
125 user_data[-1]
126 )
127 self.disp.log_debug(
128 f"stringed_datetime = {user_data}", title
129 )
130 table_columns = self.database_link.get_table_column_names(
131 CONST.TAB_CONNECTIONS
132 )
133 if isinstance(table_columns, int):
134 return self.error
135 table_columns = CONST.clean_list(
136 table_columns,
137 CONST.TABLE_COLUMNS_TO_IGNORE,
138 self.disp
139 )
140 self.disp.log_debug(
141 f"table_columns = {table_columns}", title
142 )
143 status = self.database_link.insert_or_update_data_into_table(
144 table="Connections",
145 data=user_data,
146 columns=table_columns
147 )
148 if status != self.success:
149 self.disp.log_error(
150 "Data not inserted successfully !", title
151 )
152 return self.error
153 self.disp.log_debug(
154 "Data inserted successfully.", title
155 )
156 return self.success
157
158 def log_user_in(self, email: str = '') -> Dict[str, Any]:
159 """_summary_
160 Attempt to log the user in based on the provided credentials and the database.
161
162 Args:
163 email (str): _description_: The email of the account
164
165 Returns:
166 Dict[str, Any]: _description_: The response status
167 {'status':Union[success, error], 'token':Union['some_token', '']}
168 """
169 title = "log_user_in"
170 data = {'status': self.success, 'token': ''}
171 self.boilerplate_non_http_initialised = self.runtime_manager.get_if_exists(
172 BoilerplateNonHTTP,
174 )
176 self.disp.log_error("BoilerplateNonHttp is missing")
177 raise RuntimeError("Token validation service unavailable")
178 self.disp.log_debug(f"e-mail = {email}", title)
179 token = self.boilerplate_non_http_initialised.generate_token()
180 usr_id = self.database_link.get_data_from_table(
181 CONST.TAB_ACCOUNTS,
182 "id",
183 f"email='{email}'",
184 beautify=False
185 )
186 if isinstance(usr_id, int):
187 data['status'] = self.error
188 return data
189 self.disp.log_debug(f"usr_id = {usr_id}", title)
190 lifespan = self.boilerplate_non_http_initialised.set_lifespan(
191 CONST.UA_TOKEN_LIFESPAN
192 )
193 try:
194 uid = str(int(usr_id[0][0]))
195 self.disp.log_debug(f"uid = {uid}", title)
196 except (ValueError, IndexError):
197 data['status'] = self.error
198 return data
199 usr_data = [token, uid, lifespan]
200 self.disp.log_debug(f"usr_data = {usr_data}", title)
201 data['status'] = self._insert_login_into_database(usr_data)
202 data['token'] = token
203 self.disp.log_debug(f"Response data: {data}", title)
204 return data
205
206 def get_token_if_present(self, request: Request) -> Union[str, None]:
207 """_summary_
208 Return the token if it is present.
209
210 Args:
211 request (Request): _description_: the request header created by the endpoint caller.
212
213 Returns:
214 Union[str, None]: _description_: If the token is present, a string is returned, otherwise, it is None.
215 """
216 mtoken: Union[str, None] = request.get(CONST.REQUEST_TOKEN_KEY)
217 mbearer: Union[str, None] = request.get(CONST.REQUEST_BEARER_KEY)
218 token: Union[str, None] = request.headers.get(CONST.REQUEST_TOKEN_KEY)
219 bearer: Union[str, None] = request.headers.get(
220 CONST.REQUEST_BEARER_KEY
221 )
222 msg = f"mtoken = {mtoken}, mbearer = {mbearer}"
223 msg += f", token = {token}, bearer = {bearer}"
224 self.disp.log_debug(msg, "get_token_if_present")
225 if token is None and bearer is None and token is None and bearer is None:
226 return None
227 if mbearer is not None and mbearer.startswith('Bearer '):
228 return mbearer.split(" ")[1]
229 if bearer is not None and bearer.startswith('Bearer '):
230 return bearer.split(" ")[1]
231 if token is not None:
232 return token
233 return mtoken
234
235 async def get_body(self, request: Request) -> Dict[str, Any]:
236 """
237 Get the body of a request, whether it's JSON or form data.
238 Args:
239 request (Request): The incoming request object.
240
241 Returns:
242 Dict[str, Any]: Parsed request body in dictionary format.
243 """
244 body: Dict[str, Any] = {}
245
246 try:
247 body = await request.json()
248 except Exception:
249 try:
250 form = await request.form()
251 body = dict(form)
252
253 files = await request.form()
254 body["_files"] = {}
255
256 for file_key, file_value in files.items():
257 if isinstance(file_value, UploadFile):
258 body["_files"][file_key] = {
259 "filename": file_value.filename,
260 "content_type": file_value.content_type,
261 "content": await file_value.read()
262 }
263 except Exception as form_error:
264 msg = f"Failed to parse request body: {str(form_error)}"
265 body = {"error": msg}
266 return body
267
268 def get_body_type(self, request: Request) -> Optional[str]:
269 """Retrieve the type of the request body if present.
270 Get the content type of the request body.
271 Args:
272 request (Request): The incoming request object.
273 Returns:
274 Optional[str]: The content type of the request body, or None if not present.
275 """
276 body_type = request.headers.get("content-type", None)
277 self.disp.log_debug(f"Body type: {body_type}")
278 return body_type
279
280 def log_user_out(self, token: str = "") -> Union[Dict[str, Any], bool]:
281 """_summary_
282 Attempt to log the user out based on the provided token.
283
284 Args:
285 token (str): _description_: The token of the account
286
287 Returns:
288 Dict[str, Any]: _description_: The response status
289 {'status':Union[success, error], 'msg':'message'}
290 """
291 title = "log_user_out"
292 data = {'status': self.error, 'msg': "You are not logged in !"}
293 if token == "":
294 data["msg"] = "No token provided !"
295 return data
296
297 login_table = self.database_link.get_data_from_table(
298 CONST.TAB_CONNECTIONS,
299 "*",
300 where=f"token={token}",
301 beautify=False
302 )
303 if isinstance(login_table, int):
304 return False
305 if len(login_table) != 1:
306 return False
307 self.disp.log_debug(f"login_table = {login_table}", title)
308 status = self.database_link.remove_data_from_table(
309 CONST.TAB_CONNECTIONS,
310 f"token={token}"
311 )
312 if status != self.success:
313 data["msg"] = "Data not removed successfully !"
314 self.disp.log_error(data["msg"], title)
315 return data
316 data["status"] = self.success
317 data["msg"] = "You have successfully logged out."
318 return data
Optional[BoilerplateNonHTTP] boilerplate_non_http_initialised
Definition incoming.py:52
Dict[str, Any] get_body(self, Request request)
Definition incoming.py:235
Union[str, None] get_token_if_present(self, Request request)
Definition incoming.py:206
Dict[str, Any] log_user_in(self, str email='')
Definition incoming.py:158
None __init__(self, int error=84, int success=0, bool debug=False)
Definition incoming.py:42
Union[Dict[str, Any], bool] log_user_out(self, str token="")
Definition incoming.py:280
Optional[str] get_body_type(self, Request request)
Definition incoming.py:268
int _insert_login_into_database(self, dict[str, Any] user_data)
Definition incoming.py:106