platzky 0.4.1__py3-none-any.whl → 0.4.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
platzky/db/db.py CHANGED
@@ -96,6 +96,15 @@ class DB(ABC):
96
96
  def get_font(self) -> str:
97
97
  pass
98
98
 
99
+ @abstractmethod
100
+ def health_check(self) -> None:
101
+ """Perform a health check on the database.
102
+
103
+ Should raise an exception if the database is not healthy.
104
+ This should be a lightweight operation suitable for health checks.
105
+ """
106
+ pass
107
+
99
108
 
100
109
  class DBConfig(BaseModel):
101
110
  type: str = Field(alias="TYPE")
platzky/db/graph_ql_db.py CHANGED
@@ -307,3 +307,18 @@ class GraphQL(DB):
307
307
  """
308
308
  )
309
309
  return self.client.execute(plugins_data)["pluginConfigs"]
310
+
311
+ def health_check(self) -> None:
312
+ """Perform a health check on the GraphQL database.
313
+
314
+ Raises an exception if the database is not accessible.
315
+ """
316
+ # Simple query to check connectivity
317
+ health_query = gql(
318
+ """
319
+ query {
320
+ __typename
321
+ }
322
+ """
323
+ )
324
+ self.client.execute(health_query)
platzky/db/json_db.py CHANGED
@@ -116,3 +116,11 @@ class Json(DB):
116
116
 
117
117
  def get_plugins_data(self):
118
118
  return self.data.get("plugins", [])
119
+
120
+ def health_check(self) -> None:
121
+ """Perform a health check on the JSON database.
122
+
123
+ Raises an exception if the database is not accessible.
124
+ """
125
+ # Try to access site_content to ensure basic structure is valid
126
+ self._get_site_content()
platzky/db/mongodb_db.py CHANGED
@@ -125,6 +125,14 @@ class MongoDB(DB):
125
125
  return site_content.get("font", "")
126
126
  return ""
127
127
 
128
+ def health_check(self) -> None:
129
+ """Perform a health check on the MongoDB database.
130
+
131
+ Raises an exception if the database is not accessible.
132
+ """
133
+ # Simple ping to check if database is accessible
134
+ self.client.admin.command("ping")
135
+
128
136
  def _close_connection(self) -> None:
129
137
  """Close the MongoDB connection"""
130
138
  if self.client:
platzky/engine.py CHANGED
@@ -1,7 +1,8 @@
1
1
  import os
2
- from typing import List
2
+ from concurrent.futures import ThreadPoolExecutor, TimeoutError
3
+ from typing import Any, Callable, Dict, List, Tuple
3
4
 
4
- from flask import Flask, request, session
5
+ from flask import Blueprint, Flask, jsonify, make_response, request, session
5
6
  from flask_babel import Babel
6
7
 
7
8
  from platzky.config import Config
@@ -17,6 +18,7 @@ class Engine(Flask):
17
18
  self.login_methods = []
18
19
  self.dynamic_body = ""
19
20
  self.dynamic_head = ""
21
+ self.health_checks: List[Tuple[str, Callable[[], None]]] = []
20
22
  directory = os.path.dirname(os.path.realpath(__file__))
21
23
  locale_dir = os.path.join(directory, "locale")
22
24
  config.translation_directories.append(locale_dir)
@@ -26,6 +28,7 @@ class Engine(Flask):
26
28
  locale_selector=self.get_locale,
27
29
  default_translation_directories=babel_translation_directories,
28
30
  )
31
+ self._register_default_health_endpoints()
29
32
 
30
33
  self.cms_modules: List[CmsModule] = []
31
34
  # TODO add plugins as CMS Module - all plugins should be visible from
@@ -69,3 +72,69 @@ class Engine(Flask):
69
72
 
70
73
  session["language"] = lang
71
74
  return lang
75
+
76
+ def add_health_check(self, name: str, check_function: Callable[[], None]) -> None:
77
+ """Register a health check function"""
78
+ if not callable(check_function):
79
+ raise TypeError(f"check_function must be callable, got {type(check_function)}")
80
+ self.health_checks.append((name, check_function))
81
+
82
+ def _register_default_health_endpoints(self):
83
+ """Register default health endpoints"""
84
+
85
+ health_bp = Blueprint("health", __name__)
86
+ HEALTH_CHECK_TIMEOUT = 10 # seconds
87
+
88
+ @health_bp.route("/health/liveness")
89
+ def liveness():
90
+ """Simple liveness check - is the app running?"""
91
+ return jsonify({"status": "alive"}), 200
92
+
93
+ @health_bp.route("/health/readiness")
94
+ def readiness():
95
+ """Readiness check - can the app serve traffic?"""
96
+ health_status: Dict[str, Any] = {"status": "ready", "checks": {}}
97
+ status_code = 200
98
+
99
+ executor = ThreadPoolExecutor(max_workers=1)
100
+ try:
101
+ # Database health check with timeout
102
+ future = executor.submit(self.db.health_check)
103
+ try:
104
+ future.result(timeout=HEALTH_CHECK_TIMEOUT)
105
+ health_status["checks"]["database"] = "ok"
106
+ except TimeoutError:
107
+ health_status["checks"]["database"] = "failed: timeout"
108
+ health_status["status"] = "not_ready"
109
+ status_code = 503
110
+ except Exception as e:
111
+ health_status["checks"]["database"] = f"failed: {e!s}"
112
+ health_status["status"] = "not_ready"
113
+ status_code = 503
114
+
115
+ # Run application-registered health checks
116
+ for check_name, check_func in self.health_checks:
117
+ future = executor.submit(check_func)
118
+ try:
119
+ future.result(timeout=HEALTH_CHECK_TIMEOUT)
120
+ health_status["checks"][check_name] = "ok"
121
+ except TimeoutError:
122
+ health_status["checks"][check_name] = "failed: timeout"
123
+ health_status["status"] = "not_ready"
124
+ status_code = 503
125
+ except Exception as e:
126
+ health_status["checks"][check_name] = f"failed: {e!s}"
127
+ health_status["status"] = "not_ready"
128
+ status_code = 503
129
+ finally:
130
+ # Shutdown without waiting if any futures are still running
131
+ executor.shutdown(wait=False)
132
+
133
+ return make_response(jsonify(health_status), status_code)
134
+
135
+ # Simple /health alias for liveness
136
+ @health_bp.route("/health")
137
+ def health():
138
+ return liveness()
139
+
140
+ self.register_blueprint(health_bp)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: platzky
3
- Version: 0.4.1
3
+ Version: 0.4.3
4
4
  Summary: Not only blog engine
5
5
  License: MIT
6
6
  Requires-Python: >=3.10,<4.0
@@ -10,15 +10,15 @@ platzky/blog/comment_form.py,sha256=4lkNJ_S_2DZmJBbz-NPDqahvy2Zz5AGNH2spFeGIop4,
10
10
  platzky/config.py,sha256=M3gmZI9yI-ThgmTA4RKsAPcnJwJjcWhXipYzq3hO-Hk,2346
11
11
  platzky/db/README.md,sha256=IO-LoDsd4dLBZenaz423EZjvEOQu_8m2OC0G7du170w,1753
12
12
  platzky/db/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
- platzky/db/db.py,sha256=i8qOojEjV4xBzcB7Ic34aRuREK_khJTcP8CgS5yY8DE,2785
13
+ platzky/db/db.py,sha256=0h5rGCBO_N1wBqJRl5EoiW_bFDpNIvmNwuA0hJi89jw,3060
14
14
  platzky/db/db_loader.py,sha256=CuEiXxhIa4bFMm0vi7ugzm7j3WycilGRKCU6smgIImE,905
15
15
  platzky/db/github_json_db.py,sha256=G1GBIomeKOCeG05pA4qccaFntiGzkgyEMQJz_FQlvNY,2185
16
16
  platzky/db/google_json_db.py,sha256=rS__UEK7ed71htTg066_vzpg0etTlpke6YkcrAQ3Fgk,1325
17
- platzky/db/graph_ql_db.py,sha256=znt40tifzdh6ZdR6sbU8AUyKrs75SEZRJzsImjCor54,8579
18
- platzky/db/json_db.py,sha256=-k6NcMBK99SwJObK7UA15hqVSvElrvYR1Vl7G9p0re4,3772
17
+ platzky/db/graph_ql_db.py,sha256=af6yy1R27YO8N9zJWU7VgU7optRgpdk_1ZUtab_1eT4,8967
18
+ platzky/db/json_db.py,sha256=NUBPy4jt-y37TYq4SCGaSgief3MbBWL_Efw8Bxp8Jo0,4046
19
19
  platzky/db/json_file_db.py,sha256=tPo92n5zG7vGpunn5vl66zISHBziQdxBttitvc5hPug,1030
20
- platzky/db/mongodb_db.py,sha256=AM4WJjt8rstlnJtJiyYZq8kwTXtiJpUo989cm41eGJo,4903
21
- platzky/engine.py,sha256=BrZNm2ooO0e1hp41vaABbxLWPN_WfYglTYEM5v2NkTk,2272
20
+ platzky/db/mongodb_db.py,sha256=28KO8XmTEiqE7FcNBzw_pfxOy6Vo-T7qsHdUlh59QX0,5174
21
+ platzky/engine.py,sha256=Kv242PsB8lVz_FCYdGogd8o5zGmn5Msev3B3lfYRUXA,5411
22
22
  platzky/locale/en/LC_MESSAGES/messages.po,sha256=WaZGlFAegKRq7CSz69dWKic-mKvQFhVvssvExxNmGaU,1400
23
23
  platzky/locale/pl/LC_MESSAGES/messages.po,sha256=sUPxMKDeEOoZ5UIg94rGxZD06YVWiAMWIby2XE51Hrc,1624
24
24
  platzky/models.py,sha256=DZZgKW2Q3fY2GMdikFUmAgpsRqT5VKAOwP6RmEsmO2M,1871
@@ -40,6 +40,6 @@ platzky/templates/post.html,sha256=GSgjIZsOQKtNx3cEbquSjZ5L4whPnG6MzRyoq9k4B8Q,1
40
40
  platzky/templates/robots.txt,sha256=2_j2tiYtYJnzZUrANiX9pvBxyw5Dp27fR_co18BPEJ0,116
41
41
  platzky/templates/sitemap.xml,sha256=iIJZ91_B5ZuNLCHsRtsGKZlBAXojOTP8kffqKLacgvs,578
42
42
  platzky/www_handler.py,sha256=pF6Rmvem1sdVqHD7z3RLrDuG-CwAqfGCti50_NPsB2w,725
43
- platzky-0.4.1.dist-info/METADATA,sha256=br67YHAkJvv9S-bEx6cNX-wWPzdFmNV17HhER6b-ls8,1818
44
- platzky-0.4.1.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
45
- platzky-0.4.1.dist-info/RECORD,,
43
+ platzky-0.4.3.dist-info/METADATA,sha256=RMfCt6cM7vEEWaS_lmahJZ9kNyy53c41_gmu064X6tU,1818
44
+ platzky-0.4.3.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
45
+ platzky-0.4.3.dist-info/RECORD,,