tina4-python 0.2.26__tar.gz → 0.2.28__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.
- {tina4_python-0.2.26 → tina4_python-0.2.28}/PKG-INFO +3 -2
- {tina4_python-0.2.26 → tina4_python-0.2.28}/pyproject.toml +4 -2
- {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/Auth.py +9 -4
- {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/Router.py +31 -23
- {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/Webserver.py +55 -5
- {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/__init__.py +11 -6
- tina4_python-0.2.26/tina4_python/.env +0 -7
- tina4_python-0.2.26/tina4_python/logs/debug.log +0 -1066
- tina4_python-0.2.26/tina4_python/secrets/domain.cert +0 -21
- tina4_python-0.2.26/tina4_python/secrets/private.key +0 -30
- tina4_python-0.2.26/tina4_python/secrets/public.key +0 -9
- tina4_python-0.2.26/tina4_python/test.db +0 -0
- {tina4_python-0.2.26 → tina4_python-0.2.28}/README.md +0 -0
- {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/Constant.py +0 -0
- {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/Database.py +0 -0
- {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/DatabaseResult.py +0 -0
- {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/Debug.py +0 -0
- {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/Env.py +0 -0
- {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/Localization.py +0 -0
- {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/Messages.py +0 -0
- {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/MiddleWare.py +0 -0
- {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/Migration.py +0 -0
- {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/Request.py +0 -0
- {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/Response.py +0 -0
- {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/Session.py +0 -0
- {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/ShellColors.py +0 -0
- {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/Swagger.py +0 -0
- {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/Template.py +0 -0
- {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/messages.pot +0 -0
- {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/public/css/readme.md +0 -0
- {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/public/favicon.ico +0 -0
- {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/public/images/403.png +0 -0
- {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/public/images/404.png +0 -0
- {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/public/images/logo.png +0 -0
- {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/public/images/readme.md +0 -0
- {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/public/js/readme.md +0 -0
- {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/public/js/tina4helper.js +0 -0
- {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/public/swagger/index.html +0 -0
- {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/public/swagger/oauth2-redirect.html +0 -0
- {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/templates/errors/403.twig +0 -0
- {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/templates/errors/404.twig +0 -0
- {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/templates/readme.md +0 -0
- {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/translations/en/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/translations/en/LC_MESSAGES/messages.po +0 -0
- {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/translations/fr/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-0.2.26 → tina4_python-0.2.28}/tina4_python/translations/fr/LC_MESSAGES/messages.po +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: tina4-python
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.28
|
|
4
4
|
Summary: Tina4Python - This is not another framework for Python
|
|
5
5
|
Author: Andre van Zuydam
|
|
6
6
|
Author-email: andrevanzuydam@gmail.com
|
|
@@ -9,7 +9,8 @@ Classifier: Programming Language :: Python :: 3
|
|
|
9
9
|
Classifier: Programming Language :: Python :: 3.10
|
|
10
10
|
Classifier: Programming Language :: Python :: 3.11
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
-
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Requires-Dist: Jinja2 (>=3.1.4,<4.0.0)
|
|
13
14
|
Requires-Dist: bcrypt (>=4.1.3,<5.0.0)
|
|
14
15
|
Requires-Dist: cryptography (>=42.0.5,<43.0.0)
|
|
15
16
|
Requires-Dist: libsass (>=0.22.0,<0.23.0)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "tina4-python"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.28"
|
|
4
4
|
description = "Tina4Python - This is not another framework for Python"
|
|
5
5
|
authors = ["Andre van Zuydam <andrevanzuydam@gmail.com>"]
|
|
6
6
|
readme = "README.md"
|
|
@@ -9,7 +9,7 @@ readme = "README.md"
|
|
|
9
9
|
|
|
10
10
|
[tool.poetry.dependencies]
|
|
11
11
|
python = "^3.10"
|
|
12
|
-
Jinja2 = "^3.
|
|
12
|
+
Jinja2 = "^3.1.4"
|
|
13
13
|
mypy = "^0.991"
|
|
14
14
|
libsass = "^0.22.0"
|
|
15
15
|
python-dotenv = "^1.0.1"
|
|
@@ -22,6 +22,8 @@ bcrypt = "^4.1.3"
|
|
|
22
22
|
flake8 = "^7.0.0"
|
|
23
23
|
pytest = "^7.2.0"
|
|
24
24
|
jurigged = "^0.5.3"
|
|
25
|
+
safety = "^3.2.8"
|
|
26
|
+
ruff = "^0.7.0"
|
|
25
27
|
|
|
26
28
|
[build-system]
|
|
27
29
|
requires = ["poetry-core>=1.0.0"]
|
|
@@ -127,12 +127,17 @@ class Auth:
|
|
|
127
127
|
with open(self.self_signed, "wb") as f:
|
|
128
128
|
f.write(cert.public_bytes(serialization.Encoding.PEM))
|
|
129
129
|
|
|
130
|
-
def get_token(self, payload_data):
|
|
130
|
+
def get_token(self, payload_data, expiry_minutes=0):
|
|
131
131
|
private_key = self.load_private_key()
|
|
132
132
|
now = datetime.datetime.now()
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
133
|
+
|
|
134
|
+
if not "expires" in payload_data:
|
|
135
|
+
token_limit_minutes = int(os.environ.get("TINA4_TOKEN_LIMIT", 2))
|
|
136
|
+
if expiry_minutes != 0:
|
|
137
|
+
token_limit_minutes = expiry_minutes
|
|
138
|
+
expiry_time = now + datetime.timedelta(minutes=token_limit_minutes)
|
|
139
|
+
payload_data["expires"] = expiry_time.isoformat()
|
|
140
|
+
|
|
136
141
|
token = jwt.encode(
|
|
137
142
|
payload=payload_data,
|
|
138
143
|
key=private_key,
|
|
@@ -65,9 +65,9 @@ class Router:
|
|
|
65
65
|
|
|
66
66
|
Response.headers = {}
|
|
67
67
|
Response.content = ""
|
|
68
|
-
Response.http_code = Constant.
|
|
68
|
+
Response.http_code = Constant.HTTP_NOT_FOUND
|
|
69
69
|
Response.content_type = Constant.TEXT_HTML
|
|
70
|
-
result =
|
|
70
|
+
result = Response
|
|
71
71
|
|
|
72
72
|
Debug("Root Path " + tina4_python.root_path + " " + url, method, Constant.TINA4_LOG_DEBUG)
|
|
73
73
|
tina4_python.tina4_current_request["url"] = url
|
|
@@ -165,6 +165,11 @@ class Router:
|
|
|
165
165
|
|
|
166
166
|
result = await router_response(request=Request, response=Response.Response)
|
|
167
167
|
|
|
168
|
+
# we have found a result ... make sure we reflect this if the user didn't actually put the correct http response code in
|
|
169
|
+
if result is not None:
|
|
170
|
+
if result.http_code == Constant.HTTP_NOT_FOUND:
|
|
171
|
+
result.http_code = Constant.HTTP_OK
|
|
172
|
+
|
|
168
173
|
if "middleware" in route:
|
|
169
174
|
middleware_runner = MiddleWare(route["middleware"]["class"])
|
|
170
175
|
|
|
@@ -190,36 +195,39 @@ class Router:
|
|
|
190
195
|
|
|
191
196
|
break
|
|
192
197
|
|
|
193
|
-
if result is None:
|
|
194
|
-
|
|
195
|
-
|
|
198
|
+
if result is None and old_stdout is not None:
|
|
199
|
+
sys.stdout = old_stdout
|
|
200
|
+
if buffer.getvalue() != "":
|
|
201
|
+
try:
|
|
202
|
+
return Response.Response(json.loads(buffer.getvalue()), Constant.HTTP_OK, Constant.APPLICATION_JSON)
|
|
203
|
+
except:
|
|
204
|
+
return Response.Response(buffer.getvalue(), Constant.HTTP_OK, Constant.TEXT_HTML)
|
|
196
205
|
else:
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
try:
|
|
200
|
-
return Response.Response(json.loads(buffer.getvalue()), Constant.HTTP_OK, Constant.APPLICATION_JSON)
|
|
201
|
-
except:
|
|
202
|
-
return Response.Response(buffer.getvalue(), Constant.HTTP_OK, Constant.TEXT_HTML)
|
|
206
|
+
result = Response
|
|
207
|
+
result.http_code = Constant.HTTP_NOT_FOUND
|
|
203
208
|
|
|
204
209
|
# If no route is matched, serve 404
|
|
205
210
|
if result.http_code == Constant.HTTP_NOT_FOUND:
|
|
206
211
|
# Serve twigs if the files exist
|
|
212
|
+
twig_files = []
|
|
207
213
|
if url == "/":
|
|
208
|
-
|
|
214
|
+
twig_files.append("index.twig")
|
|
209
215
|
else:
|
|
210
|
-
|
|
216
|
+
twig_files.append(url + ".twig")
|
|
217
|
+
twig_files.append(url + "index.twig")
|
|
211
218
|
|
|
212
219
|
# see if we can find the twig file
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
220
|
+
for twig_file in twig_files:
|
|
221
|
+
if os.path.isfile(tina4_python.root_path + os.sep + "src" + os.sep + "templates" + os.sep + twig_file):
|
|
222
|
+
Debug("Looking for twig file",
|
|
223
|
+
tina4_python.root_path + os.sep + "src" + os.sep + "templates" + os.sep + twig_file,
|
|
224
|
+
Constant.TINA4_LOG_DEBUG)
|
|
225
|
+
|
|
226
|
+
result.headers["Cache-Control"] = "max-age=-1, public"
|
|
227
|
+
result.headers["Pragma"] = "no-cache"
|
|
228
|
+
content = Template.render_twig_template(twig_file, {"request": tina4_python.tina4_current_request})
|
|
229
|
+
if content != "":
|
|
230
|
+
return Response.Response(content, Constant.HTTP_OK, Constant.TEXT_HTML, result.headers)
|
|
223
231
|
|
|
224
232
|
if result.http_code == Constant.HTTP_NOT_FOUND:
|
|
225
233
|
content = Template.render_twig_template(
|
|
@@ -8,7 +8,7 @@ import asyncio
|
|
|
8
8
|
import base64
|
|
9
9
|
import json
|
|
10
10
|
import os
|
|
11
|
-
import
|
|
11
|
+
import re
|
|
12
12
|
from urllib.parse import unquote_plus
|
|
13
13
|
from urllib.parse import urlparse, parse_qsl
|
|
14
14
|
import tina4_python
|
|
@@ -16,6 +16,13 @@ from tina4_python import Constant
|
|
|
16
16
|
from tina4_python.Constant import HTTP_REDIRECT
|
|
17
17
|
from tina4_python.Session import Session
|
|
18
18
|
|
|
19
|
+
def is_int(v):
|
|
20
|
+
try:
|
|
21
|
+
f=int(v)
|
|
22
|
+
except ValueError:
|
|
23
|
+
return False
|
|
24
|
+
return True
|
|
25
|
+
|
|
19
26
|
|
|
20
27
|
class Webserver:
|
|
21
28
|
async def get_content_length(self):
|
|
@@ -102,23 +109,66 @@ class Webserver:
|
|
|
102
109
|
headers = await self.get_headers(headers, self.response_protocol, Constant.HTTP_OK)
|
|
103
110
|
return headers
|
|
104
111
|
|
|
105
|
-
|
|
106
112
|
params = dict(parse_qsl(urlparse(self.path).query, keep_blank_values=True))
|
|
107
113
|
|
|
114
|
+
new_params = {}
|
|
115
|
+
new_params.update(params)
|
|
116
|
+
|
|
117
|
+
for key, value in params.items():
|
|
118
|
+
regex = r"(\w+)"
|
|
119
|
+
matches = re.finditer(regex, key)
|
|
120
|
+
start_var = new_params
|
|
121
|
+
var_names = []
|
|
122
|
+
for matchNum, match in enumerate(matches, start=0):
|
|
123
|
+
if is_int(match.group()):
|
|
124
|
+
var_names.append(int(match.group()))
|
|
125
|
+
else:
|
|
126
|
+
var_names.append(match.group())
|
|
127
|
+
|
|
128
|
+
counter = 0
|
|
129
|
+
|
|
130
|
+
while counter < len(var_names):
|
|
131
|
+
var_name = var_names[counter]
|
|
132
|
+
if not is_int(var_name):
|
|
133
|
+
if isinstance(start_var, dict) and var_name in start_var:
|
|
134
|
+
start_var = start_var[var_name]
|
|
135
|
+
else:
|
|
136
|
+
if counter+1 < len(var_names) and is_int(var_names[counter+1]) :
|
|
137
|
+
if var_name not in start_var:
|
|
138
|
+
start_var[var_name] = []
|
|
139
|
+
start_var = start_var[var_name]
|
|
140
|
+
else:
|
|
141
|
+
if counter-1 > 0 and is_int(var_names[counter-1]):
|
|
142
|
+
index = int(var_names[counter-1])
|
|
143
|
+
new_value = {var_name: value}
|
|
144
|
+
if index in range(len(start_var)):
|
|
145
|
+
start_var[index].update(new_value)
|
|
146
|
+
else:
|
|
147
|
+
while len(start_var) < index:
|
|
148
|
+
start_var.append({})
|
|
149
|
+
start_var.append(new_value)
|
|
150
|
+
start_var = start_var[index]
|
|
151
|
+
else:
|
|
152
|
+
if isinstance(start_var, dict):
|
|
153
|
+
start_var[var_name] = value
|
|
154
|
+
start_var = start_var[var_name]
|
|
155
|
+
|
|
156
|
+
counter += 1
|
|
157
|
+
|
|
158
|
+
params.update(new_params)
|
|
108
159
|
content_length = await self.get_content_length()
|
|
109
160
|
if method != Constant.TINA4_GET:
|
|
110
161
|
body = await self.get_content_body(content_length)
|
|
111
162
|
else:
|
|
112
163
|
body = None
|
|
113
164
|
|
|
114
|
-
request = {"params": params, "body": body, "raw_data": self.request, "url": self.path,
|
|
165
|
+
request = {"params": params, "body": body, "raw_data": self.request, "url": self.path,
|
|
166
|
+
"headers": self.lowercase_headers, "raw_request": self.request_raw, "raw_content": self.content_raw}
|
|
115
167
|
|
|
116
168
|
tina4_python.tina4_current_request = request
|
|
117
169
|
|
|
118
170
|
response = await self.router_handler.resolve(method, self.path, request, self.lowercase_headers, self.session)
|
|
119
171
|
|
|
120
|
-
|
|
121
|
-
|
|
122
172
|
if HTTP_REDIRECT != response.http_code:
|
|
123
173
|
self.send_header("Access-Control-Allow-Origin", "*", headers)
|
|
124
174
|
self.send_header("Access-Control-Allow-Headers",
|
|
@@ -45,7 +45,6 @@ load_env(environment)
|
|
|
45
45
|
|
|
46
46
|
print(ShellColors.bright_yellow + "Setting debug mode", os.getenv("TINA4_DEBUG_LEVEL"), ShellColors.end)
|
|
47
47
|
|
|
48
|
-
|
|
49
48
|
if importlib.util.find_spec("jurigged"):
|
|
50
49
|
import jurigged
|
|
51
50
|
|
|
@@ -61,7 +60,6 @@ localize()
|
|
|
61
60
|
Debug(Messages.MSG_ASSUMING_ROOT_PATH.format(root_path=root_path, library_path=library_path),
|
|
62
61
|
Constant.TINA4_LOG_INFO)
|
|
63
62
|
|
|
64
|
-
|
|
65
63
|
tina4_routes = {}
|
|
66
64
|
tina4_current_request = {}
|
|
67
65
|
tina4_api_key = None
|
|
@@ -139,6 +137,7 @@ if os.path.exists(root_path + os.sep + "src" + os.sep + "app"):
|
|
|
139
137
|
else:
|
|
140
138
|
Debug("Missing src/app folder", Constant.TINA4_LOG_WARNING)
|
|
141
139
|
|
|
140
|
+
|
|
142
141
|
# compile sass
|
|
143
142
|
def compile_scss():
|
|
144
143
|
try:
|
|
@@ -168,23 +167,28 @@ if os.path.exists(root_path + os.sep + "src" + os.sep + "scss"):
|
|
|
168
167
|
else:
|
|
169
168
|
Debug("Missing scss folder", Constant.TINA4_LOG_WARNING)
|
|
170
169
|
|
|
170
|
+
|
|
171
171
|
# end compile sass
|
|
172
172
|
|
|
173
173
|
|
|
174
174
|
def file_get_contents(file_path):
|
|
175
175
|
return Path(file_path).read_text()
|
|
176
176
|
|
|
177
|
+
|
|
177
178
|
# Add swagger routes
|
|
178
|
-
@get("/swagger/swagger.json")
|
|
179
|
+
@get(os.getenv("SWAGGER_ROUTE", "/swagger")+"/swagger.json")
|
|
179
180
|
async def get_swagger_json(request, response):
|
|
180
181
|
json = Swagger.get_json(request)
|
|
181
182
|
return response(json)
|
|
182
183
|
|
|
183
|
-
|
|
184
|
+
|
|
185
|
+
@get(os.getenv("SWAGGER_ROUTE", "/swagger"))
|
|
184
186
|
async def get_swagger(request, response):
|
|
185
|
-
html = file_get_contents(
|
|
187
|
+
html = file_get_contents(
|
|
188
|
+
root_path + os.sep + "src" + os.sep + "public" + os.sep + "swagger" + os.sep + "index.html")
|
|
186
189
|
return response(html)
|
|
187
190
|
|
|
191
|
+
|
|
188
192
|
def webserver(host_name, port):
|
|
189
193
|
web_server = Webserver(host_name, int(port)) # HTTPServer((host_name, int(port)), Webserver)
|
|
190
194
|
web_server.router_handler = Router()
|
|
@@ -204,7 +208,8 @@ def run_web_server(in_hostname="localhost", in_port=7145):
|
|
|
204
208
|
Debug(Messages.MSG_STARTING_WEBSERVER.format(port=in_port), Constant.TINA4_LOG_INFO)
|
|
205
209
|
webserver(in_hostname, in_port)
|
|
206
210
|
|
|
207
|
-
|
|
211
|
+
|
|
212
|
+
if os.getenv('TINA4_DEFAULT_WEBSERVER', 'True') == 'True':
|
|
208
213
|
if importlib.util.find_spec("jurigged"):
|
|
209
214
|
Debug("Jurigged enabled", Constant.TINA4_LOG_INFO)
|
|
210
215
|
jurigged.watch("./")
|