platzky 0.3.1__tar.gz → 0.3.3__tar.gz
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-0.3.1 → platzky-0.3.3}/PKG-INFO +2 -1
- platzky-0.3.3/platzky/admin/fake_login.py +82 -0
- {platzky-0.3.1 → platzky-0.3.3}/platzky/admin/templates/admin.html +1 -0
- {platzky-0.3.1 → platzky-0.3.3}/platzky/admin/templates/login.html +4 -2
- {platzky-0.3.1 → platzky-0.3.3}/platzky/blog/blog.py +0 -1
- platzky-0.3.3/platzky/db/README.md +70 -0
- platzky-0.3.3/platzky/db/github_json_db.py +69 -0
- {platzky-0.3.1 → platzky-0.3.3}/platzky/db/google_json_db.py +2 -2
- {platzky-0.3.1 → platzky-0.3.3}/platzky/db/json_db.py +2 -2
- {platzky-0.3.1 → platzky-0.3.3}/platzky/db/json_file_db.py +2 -2
- {platzky-0.3.1 → platzky-0.3.3}/platzky/engine.py +0 -1
- {platzky-0.3.1 → platzky-0.3.3}/platzky/platzky.py +10 -7
- {platzky-0.3.1 → platzky-0.3.3}/platzky/templates/base.html +11 -0
- {platzky-0.3.1 → platzky-0.3.3}/pyproject.toml +2 -1
- {platzky-0.3.1 → platzky-0.3.3}/README.md +0 -0
- {platzky-0.3.1 → platzky-0.3.3}/platzky/__init__.py +0 -0
- {platzky-0.3.1 → platzky-0.3.3}/platzky/admin/admin.py +0 -0
- {platzky-0.3.1 → platzky-0.3.3}/platzky/admin/templates/module.html +0 -0
- {platzky-0.3.1 → platzky-0.3.3}/platzky/blog/__init__.py +0 -0
- {platzky-0.3.1 → platzky-0.3.3}/platzky/blog/comment_form.py +0 -0
- {platzky-0.3.1 → platzky-0.3.3}/platzky/config.py +0 -0
- {platzky-0.3.1 → platzky-0.3.3}/platzky/db/__init__.py +0 -0
- {platzky-0.3.1 → platzky-0.3.3}/platzky/db/db.py +0 -0
- {platzky-0.3.1 → platzky-0.3.3}/platzky/db/db_loader.py +0 -0
- {platzky-0.3.1 → platzky-0.3.3}/platzky/db/graph_ql_db.py +0 -0
- {platzky-0.3.1 → platzky-0.3.3}/platzky/locale/en/LC_MESSAGES/messages.po +0 -0
- {platzky-0.3.1 → platzky-0.3.3}/platzky/locale/pl/LC_MESSAGES/messages.po +0 -0
- {platzky-0.3.1 → platzky-0.3.3}/platzky/models.py +0 -0
- {platzky-0.3.1 → platzky-0.3.3}/platzky/plugin/plugin.py +0 -0
- {platzky-0.3.1 → platzky-0.3.3}/platzky/plugin/plugin_loader.py +0 -0
- {platzky-0.3.1 → platzky-0.3.3}/platzky/seo/seo.py +0 -0
- {platzky-0.3.1 → platzky-0.3.3}/platzky/static/blog.css +0 -0
- {platzky-0.3.1 → platzky-0.3.3}/platzky/static/styles.css +0 -0
- {platzky-0.3.1 → platzky-0.3.3}/platzky/templates/404.html +0 -0
- {platzky-0.3.1 → platzky-0.3.3}/platzky/templates/blog.html +0 -0
- {platzky-0.3.1 → platzky-0.3.3}/platzky/templates/body_meta.html +0 -0
- {platzky-0.3.1 → platzky-0.3.3}/platzky/templates/dynamic_css.html +0 -0
- {platzky-0.3.1 → platzky-0.3.3}/platzky/templates/feed.xml +0 -0
- {platzky-0.3.1 → platzky-0.3.3}/platzky/templates/head_meta.html +0 -0
- {platzky-0.3.1 → platzky-0.3.3}/platzky/templates/page.html +0 -0
- {platzky-0.3.1 → platzky-0.3.3}/platzky/templates/post.html +0 -0
- {platzky-0.3.1 → platzky-0.3.3}/platzky/templates/robots.txt +0 -0
- {platzky-0.3.1 → platzky-0.3.3}/platzky/templates/sitemap.xml +0 -0
- {platzky-0.3.1 → platzky-0.3.3}/platzky/www_handler.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: platzky
|
|
3
|
-
Version: 0.3.
|
|
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
|

|
|
@@ -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
|
|
@@ -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"
|
|
@@ -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,
|
|
@@ -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
|
-
|
|
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
|
[tool.poetry]
|
|
2
2
|
name = "platzky"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.3"
|
|
4
4
|
description = "Not only blog engine"
|
|
5
5
|
authors = []
|
|
6
6
|
license = "MIT"
|
|
@@ -19,6 +19,7 @@ google-cloud-storage = "^2.5.0"
|
|
|
19
19
|
humanize = "^4.9.0"
|
|
20
20
|
pydantic = "^2.7.1"
|
|
21
21
|
deprecation = "^2.1.0"
|
|
22
|
+
pygithub = "^2.6.1"
|
|
22
23
|
|
|
23
24
|
[tool.poetry.group.dev.dependencies]
|
|
24
25
|
pytest = "^8.2.1"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|