platzky 0.3.1__py3-none-any.whl → 0.3.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.
@@ -0,0 +1,82 @@
1
+ """
2
+ Fake login functionality for development environments only.
3
+
4
+ WARNING: This module provides fake login functionality and should NEVER be used in production
5
+ environments as it bypasses proper authentication and authorization controls.
6
+ """
7
+
8
+ import os
9
+ from typing import Any, Callable
10
+
11
+ from flask import Blueprint, flash, redirect, session, url_for
12
+ from markupsafe import Markup
13
+
14
+ ROLE_ADMIN = "admin"
15
+ ROLE_NONADMIN = "nonadmin"
16
+ VALID_ROLES = [ROLE_ADMIN, ROLE_NONADMIN]
17
+
18
+
19
+ def get_fake_login_html() -> Callable[[], str]:
20
+ """Return a callable that generates HTML for fake login buttons."""
21
+
22
+ def generate_html() -> str:
23
+ admin_url = url_for("admin.handle_fake_login", role="admin")
24
+ nonadmin_url = url_for("admin.handle_fake_login", role="nonadmin")
25
+
26
+ # Rest of the code remains the same
27
+ html = f"""
28
+ <div class="col-md-6 mb-4">
29
+ <div class="card">
30
+ <div class="card-header">
31
+ Development Login
32
+ </div>
33
+ <div class="card-body">
34
+ <p class="text-danger"><strong>Warning:</strong> For development only</p>
35
+ <div class="d-flex justify-content-around">
36
+ <form method="post" action="{admin_url}" style="display: inline;">
37
+ <input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
38
+ <button type="submit" class="btn btn-primary">Login as Admin</button>
39
+ </form>
40
+ <form method="post" action="{nonadmin_url}" style="display: inline;">
41
+ <input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
42
+ <button type="submit" class="btn btn-secondary">Login as Non-Admin</button>
43
+ </form>
44
+ </div>
45
+ </div>
46
+ </div>
47
+ </div>
48
+ """
49
+ return Markup(html)
50
+
51
+ return generate_html
52
+
53
+
54
+ def setup_fake_login_routes(admin_blueprint: Blueprint) -> Blueprint:
55
+ """Add fake login routes to the provided admin_blueprint."""
56
+
57
+ env = os.environ
58
+ is_testing = "PYTEST_CURRENT_TEST" in env.keys() or env.get("FLASK_DEBUG") in (
59
+ "1",
60
+ "true",
61
+ "True",
62
+ True,
63
+ )
64
+
65
+ if not is_testing:
66
+ raise RuntimeError(
67
+ "SECURITY ERROR: Fake login routes are enabled outside of a testing environment! "
68
+ "This functionality must only be used during development or testing."
69
+ )
70
+
71
+ @admin_blueprint.route("/fake-login/<role>", methods=["POST"])
72
+ def handle_fake_login(role: str) -> Any:
73
+ if role not in VALID_ROLES:
74
+ flash(f"Invalid role: {role}. Must be one of: {', '.join(VALID_ROLES)}", "error")
75
+ return redirect(url_for("admin.admin_panel_home"))
76
+ if role == ROLE_ADMIN:
77
+ session["user"] = {"username": ROLE_ADMIN, "role": ROLE_ADMIN}
78
+ else:
79
+ session["user"] = {"username": "user", "role": ROLE_NONADMIN}
80
+ return redirect(url_for("admin.admin_panel_home"))
81
+
82
+ return admin_blueprint
@@ -17,6 +17,7 @@
17
17
 
18
18
  {% block left_panel %}
19
19
  <div id="admin-panel">
20
+ <p>User: {{ user.username }}</p>
20
21
  {% for cms_module_name, cms_entries in cms_modules.items() %}
21
22
  <div class="cms-module mb-2">
22
23
  <p>{{ cms_module_name }}</p>
@@ -12,9 +12,11 @@
12
12
 
13
13
  <div class="row align-items-center">
14
14
 
15
- {% for login_method in login_methods %}
16
- {{ login_method | safe }}
15
+ {% for method in login_methods %}
16
+ {{ method() }}
17
+
17
18
  {% endfor %}
19
+
18
20
  </div>
19
21
  </div>
20
22
 
platzky/blog/blog.py CHANGED
@@ -51,7 +51,6 @@ def create_blog_blueprint(db, blog_prefix: str, locale_func):
51
51
 
52
52
  @blog.route("/<post_slug>", methods=["GET"])
53
53
  def get_post(post_slug):
54
- post = db.get_post(post_slug)
55
54
  try:
56
55
  post = db.get_post(post_slug)
57
56
  return render_template(
platzky/db/README.md ADDED
@@ -0,0 +1,70 @@
1
+ # Platzky Database Modules
2
+
3
+ This directory contains the database abstraction layer for the Platzky application. The database modules provide a consistent interface for accessing content regardless of where it's stored.
4
+
5
+ ## Architecture
6
+
7
+ The database layer is built on an abstract base class (DB) that defines a common interface. Multiple implementations are provided for different storage backends:
8
+
9
+ - **Json**: Base implementation for JSON data sources
10
+ - **JsonFile**: Local JSON file storage
11
+ - **GithubJsonDb**: JSON files stored in GitHub repository
12
+ - **GoogleJsonDb**: JSON files stored in Google Cloud Storage
13
+ - **GraphQL**: Content stored in a GraphQL API
14
+
15
+
16
+ ## Configuration
17
+
18
+ Database configuration is specified in your application config file. Each database type has its own configuration schema.
19
+
20
+ ### JSON File Database
21
+
22
+ ```yaml
23
+ DB:
24
+ TYPE: json_file
25
+ PATH: "/path/to/data.json"
26
+
27
+ ```
28
+ ### GitHub JSON Database
29
+
30
+ ```yaml
31
+ DB:
32
+ TYPE: github_json
33
+ REPO_NAME: "username/repository"
34
+ GITHUB_TOKEN: "your_github_token"
35
+ BRANCH_NAME: "main"
36
+ PATH_TO_FILE: "data.json"
37
+ ```
38
+
39
+ ### Google JSON Database
40
+
41
+ ```yaml
42
+ DB:
43
+ TYPE: google_json
44
+ BUCKET_NAME: "your-bucket-name"
45
+ SOURCE_BLOB_NAME: "data.json"
46
+ ```
47
+
48
+ ### GraphQL Database
49
+
50
+ ```yaml
51
+ DB:
52
+ TYPE: graph_ql
53
+ CMS_ENDPOINT: "https://your-graphql-endpoint.com/api"
54
+ CMS_TOKEN: "your_graphql_token"
55
+ ```
56
+
57
+ ## Usage
58
+
59
+ The database is automatically initialized based on your configuration. The application will use the appropriate database implementation
60
+
61
+ ```python
62
+ from platzky.db.db_loader import get_db
63
+
64
+ # db_config is loaded from your application config
65
+ db = get_db(db_config)
66
+
67
+ # Now you can use any of the standard DB methods
68
+ posts = db.get_all_posts("en")
69
+ menu_items = db.get_menu_items_in_lang("en")
70
+ ```
@@ -0,0 +1,69 @@
1
+ import json
2
+
3
+ import requests
4
+ from github import Github
5
+ from pydantic import Field
6
+
7
+ from platzky.db.db import DBConfig
8
+ from platzky.db.json_db import Json as JsonDB
9
+
10
+
11
+ def db_config_type():
12
+ return GithubJsonDbConfig
13
+
14
+
15
+ class GithubJsonDbConfig(DBConfig):
16
+ github_token: str = Field(alias="GITHUB_TOKEN")
17
+ repo_name: str = Field(alias="REPO_NAME")
18
+ path_to_file: str = Field(alias="PATH_TO_FILE")
19
+ branch_name: str = Field(alias="BRANCH_NAME", default="main")
20
+
21
+
22
+ def db_from_config(config: GithubJsonDbConfig):
23
+ return GithubJsonDb(
24
+ config.github_token, config.repo_name, config.branch_name, config.path_to_file
25
+ )
26
+
27
+
28
+ def get_db(config):
29
+ github_json_db_config = GithubJsonDbConfig.model_validate(config)
30
+ return GithubJsonDb(
31
+ github_json_db_config.github_token,
32
+ github_json_db_config.repo_name,
33
+ github_json_db_config.branch_name,
34
+ github_json_db_config.path_to_file,
35
+ )
36
+
37
+
38
+ class GithubJsonDb(JsonDB):
39
+ def __init__(self, github_token: str, repo_name: str, branch_name: str, path_to_file: str):
40
+ self.branch_name = branch_name
41
+ self.repo = Github(github_token).get_repo(repo_name)
42
+ self.file_path = path_to_file
43
+
44
+ try:
45
+ file_content = self.repo.get_contents(self.file_path, ref=self.branch_name)
46
+
47
+ if isinstance(file_content, list):
48
+ raise ValueError(f"Path '{self.file_path}' points to a directory, not a file")
49
+
50
+ if file_content.content:
51
+ raw_data = file_content.decoded_content.decode("utf-8")
52
+ else:
53
+
54
+ download_url = file_content.download_url
55
+ response = requests.get(download_url, timeout=40)
56
+ response.raise_for_status()
57
+ raw_data = response.text
58
+
59
+ self.data = json.loads(raw_data)
60
+
61
+ except (json.JSONDecodeError, requests.RequestException) as e:
62
+ raise ValueError(f"Error parsing JSON content: {e}")
63
+ except Exception as e:
64
+ raise ValueError(f"Error retrieving GitHub content: {e}")
65
+
66
+ super().__init__(self.data)
67
+
68
+ self.module_name = "github_json_db"
69
+ self.db_name = "GithubJsonDb"
@@ -3,8 +3,8 @@ import json
3
3
  from google.cloud.storage import Client
4
4
  from pydantic import Field
5
5
 
6
- from .db import DBConfig
7
- from .json_db import Json
6
+ from platzky.db.db import DBConfig
7
+ from platzky.db.json_db import Json
8
8
 
9
9
 
10
10
  def db_config_type():
platzky/db/json_db.py CHANGED
@@ -3,8 +3,8 @@ from typing import Any, Dict
3
3
 
4
4
  from pydantic import Field
5
5
 
6
- from ..models import MenuItem, Post
7
- from .db import DB, DBConfig
6
+ from platzky.db.db import DB, DBConfig
7
+ from platzky.models import MenuItem, Post
8
8
 
9
9
 
10
10
  def db_config_type():
@@ -2,8 +2,8 @@ import json
2
2
 
3
3
  from pydantic import Field
4
4
 
5
- from .db import DBConfig
6
- from .json_db import Json
5
+ from platzky.db.db import DBConfig
6
+ from platzky.db.json_db import Json
7
7
 
8
8
 
9
9
  def db_config_type():
platzky/engine.py CHANGED
@@ -18,7 +18,6 @@ class Engine(Flask):
18
18
  directory = os.path.dirname(os.path.realpath(__file__))
19
19
  locale_dir = os.path.join(directory, "locale")
20
20
  config.translation_directories.append(locale_dir)
21
-
22
21
  babel_translation_directories = ";".join(config.translation_directories)
23
22
  self.babel = Babel(
24
23
  self,
platzky/platzky.py CHANGED
@@ -81,10 +81,19 @@ def create_engine(config: Config, db) -> Engine:
81
81
 
82
82
 
83
83
  def create_app_from_config(config: Config) -> Engine:
84
- engine = create_engine_from_config(config)
84
+ db = get_db(config.db)
85
+ engine = create_engine(config, db)
86
+
85
87
  admin_blueprint = admin.create_admin_blueprint(
86
88
  login_methods=engine.login_methods, db=engine.db, locale_func=engine.get_locale
87
89
  )
90
+
91
+ if config.feature_flags and config.feature_flags.get("FAKE_LOGIN", False):
92
+ from platzky.admin.fake_login import get_fake_login_html, setup_fake_login_routes
93
+
94
+ engine.login_methods.append(get_fake_login_html())
95
+ admin_blueprint = setup_fake_login_routes(admin_blueprint)
96
+
88
97
  blog_blueprint = blog.create_blog_blueprint(
89
98
  db=engine.db,
90
99
  blog_prefix=config.blog_prefix,
@@ -101,12 +110,6 @@ def create_app_from_config(config: Config) -> Engine:
101
110
  return engine
102
111
 
103
112
 
104
- def create_engine_from_config(config: Config) -> Engine:
105
- """Create an engine from a config."""
106
- db = get_db(config.db)
107
- return create_engine(config, db)
108
-
109
-
110
113
  def create_app(config_path: str) -> Engine:
111
114
  config = Config.parse_yaml(config_path)
112
115
  return create_app_from_config(config)
@@ -14,6 +14,17 @@
14
14
  {% block body_meta %}
15
15
  {% include "body_meta.html" %}
16
16
  {% endblock %}
17
+
18
+ {% with messages = get_flashed_messages(with_categories=true) %}
19
+ {% if messages %}
20
+ <div class="flash-messages">
21
+ {% for category, message in messages %}
22
+ <div class="alert alert-{{ category }}">{{ message }}</div>
23
+ {% endfor %}
24
+ </div>
25
+ {% endif %}
26
+ {% endwith %}
27
+
17
28
  <div class="container-fluid d-flex flex-column h-100 g-0">
18
29
  <div class="row header-row bg-light g-0">
19
30
  <nav class="navbar navbar-expand-lg navbar-light px-3 py-1" id="mainNav">
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: platzky
3
- Version: 0.3.1
3
+ Version: 0.3.3
4
4
  Summary: Not only blog engine
5
5
  License: MIT
6
6
  Requires-Python: >=3.10,<4.0
@@ -21,6 +21,7 @@ Requires-Dist: google-cloud-storage (>=2.5.0,<3.0.0)
21
21
  Requires-Dist: gql (>=3.4.0,<4.0.0)
22
22
  Requires-Dist: humanize (>=4.9.0,<5.0.0)
23
23
  Requires-Dist: pydantic (>=2.7.1,<3.0.0)
24
+ Requires-Dist: pygithub (>=2.6.1,<3.0.0)
24
25
  Description-Content-Type: text/markdown
25
26
 
26
27
  ![Github Actions](https://github.com/platzky/platzky/actions/workflows/tests.yml/badge.svg?event=push&branch=main)
@@ -1,31 +1,34 @@
1
1
  platzky/__init__.py,sha256=IhL91rSWxIIJQNfVsqJ1d4yY5D2WyWcefo4Xv2aX_lo,180
2
2
  platzky/admin/admin.py,sha256=nQq0IcBhrcX6S4gd71MejOcc2yizVv20UmF4ZRvZnBk,1019
3
- platzky/admin/templates/admin.html,sha256=aIeQI9lSFvj6kwZg8U_AE7VSiVCxZeUdB_gLXzZDU20,778
4
- platzky/admin/templates/login.html,sha256=a8dpJabAjvKyJZt9wKt9GtEO2XShuvo2m6BvZqDH6bU,391
3
+ platzky/admin/fake_login.py,sha256=a9mJFwqk4f5rAcRAY9E_maSd4BYZWvWdf_22IUBcvn4,3011
4
+ platzky/admin/templates/admin.html,sha256=4WdatUbuWakR3Yhrr7ClzKlUMXVcYdl_2kMBzW_faM0,813
5
+ platzky/admin/templates/login.html,sha256=oBNuv130iMTwXrtRnDUDcGIGvu0O2VsIbjQxw-Tjd7Y,380
5
6
  platzky/admin/templates/module.html,sha256=WuQZxKQDD4INl-QF2uiKHf9Fmf2h7cEW9RLe1nWKC8k,175
6
7
  platzky/blog/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
- platzky/blog/blog.py,sha256=caBUewnwd6QrJDv20m4JDfDDjiVu7hM0AJnoTyCdwM4,3009
8
+ platzky/blog/blog.py,sha256=L9IYWxnLo1v1h_wLF-0HyG1Y4RSGg7maEMnYxhTgG5Y,2971
8
9
  platzky/blog/comment_form.py,sha256=4lkNJ_S_2DZmJBbz-NPDqahvy2Zz5AGNH2spFeGIop4,513
9
10
  platzky/config.py,sha256=M3gmZI9yI-ThgmTA4RKsAPcnJwJjcWhXipYzq3hO-Hk,2346
11
+ platzky/db/README.md,sha256=IO-LoDsd4dLBZenaz423EZjvEOQu_8m2OC0G7du170w,1753
10
12
  platzky/db/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
13
  platzky/db/db.py,sha256=o3jXcA97MwOepJhxtmXpyfDATRcMcJmX6O1Bro5-Rkw,2796
12
14
  platzky/db/db_loader.py,sha256=CuEiXxhIa4bFMm0vi7ugzm7j3WycilGRKCU6smgIImE,905
13
- platzky/db/google_json_db.py,sha256=phRW8196o3lXxO3oSBAMMXfbOKR3K2MGkzF6w41SPBc,1305
15
+ platzky/db/github_json_db.py,sha256=G1GBIomeKOCeG05pA4qccaFntiGzkgyEMQJz_FQlvNY,2185
16
+ platzky/db/google_json_db.py,sha256=rS__UEK7ed71htTg066_vzpg0etTlpke6YkcrAQ3Fgk,1325
14
17
  platzky/db/graph_ql_db.py,sha256=aGE1glBmLmx4mE1aysGe6sl0lP2uG89LUnu2hmkdvqk,8528
15
- platzky/db/json_db.py,sha256=J3sKykFLIv4tors0kEYF55SHLviLhHg8m-2fljXvqFI,3756
16
- platzky/db/json_file_db.py,sha256=UQ8TadELmqOzj_tgNfmzhtCkDkMAgcB9vaUy0GQXUY4,1010
17
- platzky/engine.py,sha256=x6cP1oCaqqamfFgDdup7R9pe8z1KafdbNvrcUEOX7Ns,1897
18
+ platzky/db/json_db.py,sha256=-k6NcMBK99SwJObK7UA15hqVSvElrvYR1Vl7G9p0re4,3772
19
+ platzky/db/json_file_db.py,sha256=tPo92n5zG7vGpunn5vl66zISHBziQdxBttitvc5hPug,1030
20
+ platzky/engine.py,sha256=mweAkMG-DCei84rXfggukcsMyje4rj9rSk5v5AwnF04,1896
18
21
  platzky/locale/en/LC_MESSAGES/messages.po,sha256=WaZGlFAegKRq7CSz69dWKic-mKvQFhVvssvExxNmGaU,1400
19
22
  platzky/locale/pl/LC_MESSAGES/messages.po,sha256=sUPxMKDeEOoZ5UIg94rGxZD06YVWiAMWIby2XE51Hrc,1624
20
23
  platzky/models.py,sha256=-IIlyeLzACeTUpzuzvzJYxtT57E6wRiERoRgXJYMMtY,1502
21
- platzky/platzky.py,sha256=VBLN71eq81EDV5qQYwKUX_InQffmrouAXxblyoZpb-4,3666
24
+ platzky/platzky.py,sha256=wy79uXK3qignoRn7jj-bJxSRSGsS_qqsTRzRZihd2UY,3819
22
25
  platzky/plugin/plugin.py,sha256=tV8aobIzMDJe1frKUAi4kLbrTAIS0FWE3oYpktSo6Ug,1633
23
26
  platzky/plugin/plugin_loader.py,sha256=MeQ8LNbrOomwXgc1ISHuyhjZd2mzYKen70eDShWs-Co,3497
24
27
  platzky/seo/seo.py,sha256=N_MmAA4KJZmmrDUh0hYNtD8ycOwpNKow4gVSAv8V3N4,2631
25
28
  platzky/static/blog.css,sha256=TrppzgQbj4UtuTufDCdblyNTVAqgIbhD66Cziyv_xnY,7893
26
29
  platzky/static/styles.css,sha256=U5ddGIK-VcGRJZ3BdOpMp0pR__k6rNEMsuQXkP4tFQ0,686
27
30
  platzky/templates/404.html,sha256=EheoLSWylOscLH8FmcMA4c6Jw14i5HkSvE_GXzGIrUo,78
28
- platzky/templates/base.html,sha256=0YuB7EG_5QIK5RZ4n_HTe0Iwa1zjbhhuZd9YYJGqhog,3711
31
+ platzky/templates/base.html,sha256=clvWlVOxNLqSQxBpPao3qnKKzkU2q48Apf1WbHJgYfE,4003
29
32
  platzky/templates/blog.html,sha256=aPl-DzLX85bHv7tN8UjlABR086PUJ9IGlGbIBioFHGA,1281
30
33
  platzky/templates/body_meta.html,sha256=bRHG_BF-jloBLXASrpJVO6hDfpzq3MqMpJTXt9fLHJk,323
31
34
  platzky/templates/dynamic_css.html,sha256=a1AeodEVwM_JAw1ySQkYN1xnKLydTXm2YzQwmjqlSyg,629
@@ -36,6 +39,6 @@ platzky/templates/post.html,sha256=GSgjIZsOQKtNx3cEbquSjZ5L4whPnG6MzRyoq9k4B8Q,1
36
39
  platzky/templates/robots.txt,sha256=2_j2tiYtYJnzZUrANiX9pvBxyw5Dp27fR_co18BPEJ0,116
37
40
  platzky/templates/sitemap.xml,sha256=iIJZ91_B5ZuNLCHsRtsGKZlBAXojOTP8kffqKLacgvs,578
38
41
  platzky/www_handler.py,sha256=pF6Rmvem1sdVqHD7z3RLrDuG-CwAqfGCti50_NPsB2w,725
39
- platzky-0.3.1.dist-info/METADATA,sha256=iHkJnppWm5npC0Y-fJQywiO9toIVeoxQdbH0S3cAGhk,1686
40
- platzky-0.3.1.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
41
- platzky-0.3.1.dist-info/RECORD,,
42
+ platzky-0.3.3.dist-info/METADATA,sha256=T_xvNjmWYBFSwwgm5MplpGkF0_pUnVQsPlcoiqllbd4,1727
43
+ platzky-0.3.3.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
44
+ platzky-0.3.3.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 2.1.1
2
+ Generator: poetry-core 2.1.3
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any