tina4-python 0.2.122__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.
- tina4_python/Auth.py +222 -0
- tina4_python/Constant.py +43 -0
- tina4_python/Database.py +591 -0
- tina4_python/DatabaseResult.py +107 -0
- tina4_python/DatabaseTypes.py +15 -0
- tina4_python/Debug.py +126 -0
- tina4_python/Env.py +37 -0
- tina4_python/Localization.py +42 -0
- tina4_python/Messages.py +30 -0
- tina4_python/MiddleWare.py +90 -0
- tina4_python/Migration.py +107 -0
- tina4_python/ORM.py +639 -0
- tina4_python/Queue.py +615 -0
- tina4_python/Request.py +19 -0
- tina4_python/Response.py +121 -0
- tina4_python/Router.py +423 -0
- tina4_python/Session.py +342 -0
- tina4_python/ShellColors.py +20 -0
- tina4_python/Swagger.py +228 -0
- tina4_python/Template.py +107 -0
- tina4_python/Webserver.py +429 -0
- tina4_python/Websocket.py +49 -0
- tina4_python/__init__.py +392 -0
- tina4_python/messages.pot +83 -0
- tina4_python/public/css/readme.md +0 -0
- tina4_python/public/favicon.ico +0 -0
- tina4_python/public/images/403.png +0 -0
- tina4_python/public/images/404.png +0 -0
- tina4_python/public/images/500.png +0 -0
- tina4_python/public/images/logo.png +0 -0
- tina4_python/public/images/readme.md +0 -0
- tina4_python/public/js/readme.md +0 -0
- tina4_python/public/js/reconnecting-websocket.js +365 -0
- tina4_python/public/js/tina4helper.js +397 -0
- tina4_python/public/swagger/index.html +90 -0
- tina4_python/public/swagger/oauth2-redirect.html +63 -0
- tina4_python/templates/errors/403.twig +10 -0
- tina4_python/templates/errors/404.twig +10 -0
- tina4_python/templates/errors/500.twig +11 -0
- tina4_python/templates/readme.md +1 -0
- tina4_python/translations/en/LC_MESSAGES/messages.mo +0 -0
- tina4_python/translations/en/LC_MESSAGES/messages.po +80 -0
- tina4_python/translations/fr/LC_MESSAGES/messages.mo +0 -0
- tina4_python/translations/fr/LC_MESSAGES/messages.po +84 -0
- tina4_python-0.2.122.dist-info/METADATA +465 -0
- tina4_python-0.2.122.dist-info/RECORD +47 -0
- tina4_python-0.2.122.dist-info/WHEEL +4 -0
tina4_python/Session.py
ADDED
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Tina4 - This is not a 4ramework.
|
|
3
|
+
# Copy-right 2007 - current Tina4
|
|
4
|
+
# License: MIT https://opensource.org/licenses/MIT
|
|
5
|
+
#
|
|
6
|
+
# flake8: noqa: E501
|
|
7
|
+
import os
|
|
8
|
+
from http import cookies
|
|
9
|
+
import sys
|
|
10
|
+
import importlib
|
|
11
|
+
import hashlib
|
|
12
|
+
import tina4_python
|
|
13
|
+
from tina4_python.Debug import Debug
|
|
14
|
+
from tina4_python import Constant
|
|
15
|
+
|
|
16
|
+
class SessionHandler(object):
|
|
17
|
+
"""
|
|
18
|
+
Base class for session handling.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
@staticmethod
|
|
22
|
+
def load(session, _hash):
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
@staticmethod
|
|
26
|
+
def set(session, _key, _value):
|
|
27
|
+
try:
|
|
28
|
+
session.session_values[_key] = _value
|
|
29
|
+
session.save()
|
|
30
|
+
return True
|
|
31
|
+
except Exception:
|
|
32
|
+
return False
|
|
33
|
+
|
|
34
|
+
@staticmethod
|
|
35
|
+
def unset(session, _key):
|
|
36
|
+
if _key in session.session_values:
|
|
37
|
+
del session.session_values[_key]
|
|
38
|
+
session.save()
|
|
39
|
+
return True
|
|
40
|
+
else:
|
|
41
|
+
return False
|
|
42
|
+
|
|
43
|
+
@staticmethod
|
|
44
|
+
def get(session, _key):
|
|
45
|
+
if _key in session.session_values:
|
|
46
|
+
return session.session_values[_key]
|
|
47
|
+
else:
|
|
48
|
+
return None
|
|
49
|
+
|
|
50
|
+
@staticmethod
|
|
51
|
+
def close(session):
|
|
52
|
+
pass
|
|
53
|
+
|
|
54
|
+
@staticmethod
|
|
55
|
+
def save(session):
|
|
56
|
+
pass
|
|
57
|
+
|
|
58
|
+
class SessionFileHandler(SessionHandler):
|
|
59
|
+
"""
|
|
60
|
+
Session File Handler
|
|
61
|
+
"""
|
|
62
|
+
@staticmethod
|
|
63
|
+
def load(session, _hash):
|
|
64
|
+
session.session_hash = _hash
|
|
65
|
+
if os.path.isfile(session.session_path + os.sep + _hash):
|
|
66
|
+
with open(session.session_path + os.sep + _hash, "r") as file:
|
|
67
|
+
token = file.read()
|
|
68
|
+
file.close()
|
|
69
|
+
if tina4_python.tina4_auth.valid(token):
|
|
70
|
+
payload = tina4_python.tina4_auth.get_payload(token)
|
|
71
|
+
for key in payload:
|
|
72
|
+
if key != "expires":
|
|
73
|
+
session.set(key, payload[key])
|
|
74
|
+
else:
|
|
75
|
+
Debug("Session expired, starting a new one", Constant.TINA4_LOG_DEBUG)
|
|
76
|
+
session.start(_hash)
|
|
77
|
+
else:
|
|
78
|
+
Debug("Cannot load session, starting a new one", Constant.TINA4_LOG_DEBUG)
|
|
79
|
+
session.start(_hash)
|
|
80
|
+
|
|
81
|
+
@staticmethod
|
|
82
|
+
def close(session):
|
|
83
|
+
try:
|
|
84
|
+
if os.path.isfile(session.session_path + os.sep + session.session_hash):
|
|
85
|
+
os.remove(session.session_path + os.sep + session.session_hash)
|
|
86
|
+
return True
|
|
87
|
+
except Exception:
|
|
88
|
+
return False
|
|
89
|
+
|
|
90
|
+
@staticmethod
|
|
91
|
+
def save(session):
|
|
92
|
+
try:
|
|
93
|
+
if not os.path.exists(session.session_path):
|
|
94
|
+
os.makedirs(session.session_path)
|
|
95
|
+
token = tina4_python.tina4_auth.get_token(payload_data=session.session_values)
|
|
96
|
+
with open(session.session_path + os.sep + session.session_hash, "w") as file:
|
|
97
|
+
file.write(token)
|
|
98
|
+
file.close()
|
|
99
|
+
return True
|
|
100
|
+
except Exception as E:
|
|
101
|
+
Debug("Session save failure", E, Constant.TINA4_LOG_ERROR)
|
|
102
|
+
return False
|
|
103
|
+
|
|
104
|
+
class SessionRedisHandler(SessionHandler):
|
|
105
|
+
|
|
106
|
+
@staticmethod
|
|
107
|
+
def __init_redis():
|
|
108
|
+
try:
|
|
109
|
+
redis = importlib.import_module("redis")
|
|
110
|
+
except Exception as e:
|
|
111
|
+
Debug("Redis not installed, install with pip install redis or poetry add redis", str(e), Constant.TINA4_LOG_ERROR)
|
|
112
|
+
sys.exit(1)
|
|
113
|
+
|
|
114
|
+
if os.getenv("TINA4_SESSION_REDIS_SECRET", "") != "":
|
|
115
|
+
redis_instance = redis.Redis(host=os.getenv("TINA4_SESSION_REDIS_HOST", "localhost"),
|
|
116
|
+
port=os.getenv("TINA4_SESSION_REDIS_PORT",6379),
|
|
117
|
+
password=os.getenv("TINA4_SESSION_REDIS_SECRET", ""),
|
|
118
|
+
decode_responses=True)
|
|
119
|
+
else:
|
|
120
|
+
redis_instance = redis.Redis(host=os.getenv("TINA4_SESSION_REDIS_HOST", "localhost"),
|
|
121
|
+
port=os.getenv("TINA4_SESSION_REDIS_PORT",6379),
|
|
122
|
+
decode_responses=True)
|
|
123
|
+
return redis_instance
|
|
124
|
+
|
|
125
|
+
"""
|
|
126
|
+
Session Redis Handler
|
|
127
|
+
"""
|
|
128
|
+
@staticmethod
|
|
129
|
+
def load(session, _hash):
|
|
130
|
+
"""
|
|
131
|
+
Loads the redis session
|
|
132
|
+
:param session:
|
|
133
|
+
:param _hash:
|
|
134
|
+
:return:
|
|
135
|
+
"""
|
|
136
|
+
try:
|
|
137
|
+
session.session_hash = _hash
|
|
138
|
+
r = SessionRedisHandler.__init_redis()
|
|
139
|
+
token = r.get(_hash)
|
|
140
|
+
if tina4_python.tina4_auth.valid(token):
|
|
141
|
+
payload = tina4_python.tina4_auth.get_payload(token)
|
|
142
|
+
for key in payload:
|
|
143
|
+
if key != "expires":
|
|
144
|
+
session.set(key, payload[key])
|
|
145
|
+
else:
|
|
146
|
+
Debug("Session expired, starting a new one", Constant.TINA4_LOG_WARNING)
|
|
147
|
+
_hash = None
|
|
148
|
+
session.start(_hash)
|
|
149
|
+
except Exception:
|
|
150
|
+
Debug("Redis not available, sessions will fail", Constant.TINA4_LOG_ERROR)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
@staticmethod
|
|
154
|
+
def close(session):
|
|
155
|
+
"""
|
|
156
|
+
Closes the redis session
|
|
157
|
+
:param session:
|
|
158
|
+
:return:
|
|
159
|
+
"""
|
|
160
|
+
r = SessionRedisHandler.__init_redis()
|
|
161
|
+
try:
|
|
162
|
+
r.set(session.session_hash, "")
|
|
163
|
+
return True
|
|
164
|
+
except Exception:
|
|
165
|
+
return False
|
|
166
|
+
|
|
167
|
+
@staticmethod
|
|
168
|
+
def save(session):
|
|
169
|
+
"""
|
|
170
|
+
Saves the redis session
|
|
171
|
+
:param session:
|
|
172
|
+
:return:
|
|
173
|
+
"""
|
|
174
|
+
r = SessionRedisHandler.__init_redis()
|
|
175
|
+
try:
|
|
176
|
+
token = tina4_python.tina4_auth.get_token(payload_data=session.session_values)
|
|
177
|
+
r.set(session.session_hash, token)
|
|
178
|
+
return True
|
|
179
|
+
except Exception as e:
|
|
180
|
+
Debug("Session save failure", str(e), Constant.TINA4_LOG_ERROR)
|
|
181
|
+
return False
|
|
182
|
+
|
|
183
|
+
class SessionValkeyHandler(SessionHandler):
|
|
184
|
+
|
|
185
|
+
@staticmethod
|
|
186
|
+
def __init_valkey():
|
|
187
|
+
try:
|
|
188
|
+
valkey = importlib.import_module("valkey")
|
|
189
|
+
except Exception as e:
|
|
190
|
+
Debug("Valkey not installed, install with pip/uv", str(e), Constant.TINA4_LOG_ERROR)
|
|
191
|
+
sys.exit(1)
|
|
192
|
+
|
|
193
|
+
params = {
|
|
194
|
+
"host": os.getenv("TINA4_SESSION_VALKEY_HOST", "localhost"),
|
|
195
|
+
"port": int(os.getenv("TINA4_SESSION_VALKEY_PORT", 6379)),
|
|
196
|
+
"decode_responses": True
|
|
197
|
+
}
|
|
198
|
+
if os.getenv("TINA4_SESSION_VALKEY_SECRET", ""):
|
|
199
|
+
params["password"] = os.getenv("TINA4_SESSION_VALKEY_SECRET", "")
|
|
200
|
+
params["username"] = os.getenv("TINA4_SESSION_VALKEY_USER", "default")
|
|
201
|
+
|
|
202
|
+
if os.getenv("TINA4_SESSION_VALKEY_SSL", "False").upper() == "TRUE":
|
|
203
|
+
params["ssl"] = True
|
|
204
|
+
|
|
205
|
+
valkey_instance = valkey.Valkey(**params)
|
|
206
|
+
|
|
207
|
+
return valkey_instance
|
|
208
|
+
|
|
209
|
+
"""
|
|
210
|
+
Session Valkey Handler
|
|
211
|
+
"""
|
|
212
|
+
@staticmethod
|
|
213
|
+
def load(session, _hash):
|
|
214
|
+
"""
|
|
215
|
+
Loads the Valkey session
|
|
216
|
+
:param session:
|
|
217
|
+
:param _hash:
|
|
218
|
+
:return:
|
|
219
|
+
"""
|
|
220
|
+
try:
|
|
221
|
+
session.session_hash = _hash
|
|
222
|
+
r = SessionValkeyHandler.__init_valkey()
|
|
223
|
+
token = r.get(_hash)
|
|
224
|
+
if tina4_python.tina4_auth.valid(token):
|
|
225
|
+
payload = tina4_python.tina4_auth.get_payload(token)
|
|
226
|
+
for key in payload:
|
|
227
|
+
if key != "expires":
|
|
228
|
+
session.set(key, payload[key])
|
|
229
|
+
else:
|
|
230
|
+
Debug("Session expired, starting a new one", Constant.TINA4_LOG_DEBUG)
|
|
231
|
+
session.start(_hash)
|
|
232
|
+
except Exception:
|
|
233
|
+
Debug("Valkey not available, sessions will fail", Constant.TINA4_LOG_ERROR)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
@staticmethod
|
|
237
|
+
def close(session):
|
|
238
|
+
"""
|
|
239
|
+
Closes the Valkey session
|
|
240
|
+
:param session:
|
|
241
|
+
:return:
|
|
242
|
+
"""
|
|
243
|
+
r = SessionValkeyHandler.__init_valkey()
|
|
244
|
+
try:
|
|
245
|
+
r.set(session.session_hash, "")
|
|
246
|
+
return True
|
|
247
|
+
except Exception:
|
|
248
|
+
return False
|
|
249
|
+
|
|
250
|
+
@staticmethod
|
|
251
|
+
def save(session):
|
|
252
|
+
"""
|
|
253
|
+
Saves the Valkey session
|
|
254
|
+
:param session:
|
|
255
|
+
:return:
|
|
256
|
+
"""
|
|
257
|
+
r = SessionValkeyHandler.__init_valkey()
|
|
258
|
+
try:
|
|
259
|
+
token = tina4_python.tina4_auth.get_token(payload_data=session.session_values)
|
|
260
|
+
r.set(session.session_hash, token)
|
|
261
|
+
return True
|
|
262
|
+
except Exception as e:
|
|
263
|
+
Debug("Session save failure", str(e), Constant.TINA4_LOG_ERROR)
|
|
264
|
+
return False
|
|
265
|
+
|
|
266
|
+
class Session:
|
|
267
|
+
|
|
268
|
+
def __init__(self, _default_name="PY_SESS", _default_path="sessions", _default_handler="SessionFileHandler"):
|
|
269
|
+
self.session_name = _default_name
|
|
270
|
+
self.cookie = cookies.SimpleCookie()
|
|
271
|
+
self.session_path = _default_path
|
|
272
|
+
self.session_values = {}
|
|
273
|
+
self.session_hash = ""
|
|
274
|
+
self.default_handler = _default_handler
|
|
275
|
+
exec("self.default_handler = "+_default_handler)
|
|
276
|
+
|
|
277
|
+
def start(self, _hash=None):
|
|
278
|
+
# create a file for the session?
|
|
279
|
+
# set the cookie for the session
|
|
280
|
+
token = tina4_python.tina4_auth.get_token(payload_data=self.session_values)
|
|
281
|
+
if _hash is None:
|
|
282
|
+
file_hash = hashlib.md5(token.encode()).hexdigest()
|
|
283
|
+
else:
|
|
284
|
+
file_hash = _hash
|
|
285
|
+
self.session_hash = file_hash
|
|
286
|
+
self.save()
|
|
287
|
+
|
|
288
|
+
return file_hash
|
|
289
|
+
|
|
290
|
+
def load(self, _hash):
|
|
291
|
+
"""
|
|
292
|
+
Loads a session based on the hash
|
|
293
|
+
:param _hash:
|
|
294
|
+
:return:
|
|
295
|
+
"""
|
|
296
|
+
self.default_handler.load(self, _hash)
|
|
297
|
+
|
|
298
|
+
def set(self, _key, _value):
|
|
299
|
+
"""
|
|
300
|
+
Sets a session key value
|
|
301
|
+
:param _key:
|
|
302
|
+
:param _value:
|
|
303
|
+
:return:
|
|
304
|
+
"""
|
|
305
|
+
return self.default_handler.set(self, _key, _value)
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def unset(self, _key):
|
|
309
|
+
"""
|
|
310
|
+
Unsets the session key
|
|
311
|
+
:param _key:
|
|
312
|
+
:return:
|
|
313
|
+
"""
|
|
314
|
+
return self.default_handler.unset(self, _key)
|
|
315
|
+
|
|
316
|
+
def get(self, _key):
|
|
317
|
+
"""
|
|
318
|
+
Returns false if session cannot be retrieved
|
|
319
|
+
:param _key:
|
|
320
|
+
:return:
|
|
321
|
+
"""
|
|
322
|
+
return self.default_handler.get(self, _key)
|
|
323
|
+
|
|
324
|
+
def close(self):
|
|
325
|
+
"""
|
|
326
|
+
Close the session and remove the file or record
|
|
327
|
+
:return:
|
|
328
|
+
"""
|
|
329
|
+
return self.default_handler.close(self)
|
|
330
|
+
|
|
331
|
+
def save(self):
|
|
332
|
+
"""
|
|
333
|
+
Saves the session information
|
|
334
|
+
:return:
|
|
335
|
+
"""
|
|
336
|
+
return self.default_handler.save(self)
|
|
337
|
+
|
|
338
|
+
def __iter__(self):
|
|
339
|
+
for key, value in self.session_values.items():
|
|
340
|
+
if key != "expires":
|
|
341
|
+
yield key, value
|
|
342
|
+
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
class ShellColors:
|
|
2
|
+
end = '\033[0m'
|
|
3
|
+
bold = '\033[1m'
|
|
4
|
+
under_line = '\033[4m'
|
|
5
|
+
black = "\033[0;30m"
|
|
6
|
+
red = "\033[0;31m"
|
|
7
|
+
green = "\033[0;32m"
|
|
8
|
+
yellow = "\033[0;33m"
|
|
9
|
+
blue = "\033[0;34m"
|
|
10
|
+
magenta = "\033[0;35m"
|
|
11
|
+
cyan = "\033[0;36m"
|
|
12
|
+
white = "\033[0;37m"
|
|
13
|
+
bright_black = "\033[0;90m"
|
|
14
|
+
bright_red = "\033[0;91m"
|
|
15
|
+
bright_green = "\033[0;92m"
|
|
16
|
+
bright_yellow = "\033[0;93m"
|
|
17
|
+
bright_blue = "\033[0;94m"
|
|
18
|
+
bright_magenta = "\033[0;95m"
|
|
19
|
+
bright_cyan = "\033[0;96m"
|
|
20
|
+
bright_white = "\033[0;97m"
|
tina4_python/Swagger.py
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Tina4 - This is not a 4ramework.
|
|
3
|
+
# Copy-right 2007 - current Tina4
|
|
4
|
+
# License: MIT https://opensource.org/licenses/MIT
|
|
5
|
+
#
|
|
6
|
+
# flake8: noqa: E501
|
|
7
|
+
import os
|
|
8
|
+
import re
|
|
9
|
+
import tina4_python
|
|
10
|
+
from tina4_python import Constant, Debug
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Swagger:
|
|
14
|
+
@staticmethod
|
|
15
|
+
def set_swagger_value(callback, key_name, value):
|
|
16
|
+
if callback not in tina4_python.tina4_routes:
|
|
17
|
+
tina4_python.tina4_routes[callback] = {}
|
|
18
|
+
if "swagger" not in tina4_python.tina4_routes[callback]:
|
|
19
|
+
tina4_python.tina4_routes[callback]["swagger"] = {}
|
|
20
|
+
tina4_python.tina4_routes[callback]["swagger"][key_name] = value
|
|
21
|
+
|
|
22
|
+
@staticmethod
|
|
23
|
+
def add_descripton(description, callback):
|
|
24
|
+
Swagger.set_swagger_value(callback, "description", description)
|
|
25
|
+
|
|
26
|
+
@staticmethod
|
|
27
|
+
def add_summary(summary, callback):
|
|
28
|
+
Swagger.set_swagger_value(callback, "summary", summary)
|
|
29
|
+
|
|
30
|
+
@staticmethod
|
|
31
|
+
def add_secure(callback):
|
|
32
|
+
Swagger.set_swagger_value(callback, "secure", True)
|
|
33
|
+
|
|
34
|
+
@staticmethod
|
|
35
|
+
def add_tags(tags, callback):
|
|
36
|
+
Swagger.set_swagger_value(callback, "tags", tags)
|
|
37
|
+
|
|
38
|
+
@staticmethod
|
|
39
|
+
def add_example(example, callback):
|
|
40
|
+
Swagger.set_swagger_value(callback, "example", example)
|
|
41
|
+
|
|
42
|
+
@staticmethod
|
|
43
|
+
def add_params(params, callback):
|
|
44
|
+
Swagger.set_swagger_value(callback, "params", params)
|
|
45
|
+
|
|
46
|
+
@staticmethod
|
|
47
|
+
def get_path_inputs(route_path):
|
|
48
|
+
url_segments = route_path.strip('/').split('/')
|
|
49
|
+
route_segments = route_path.strip('/').split('/')
|
|
50
|
+
try:
|
|
51
|
+
variables = {}
|
|
52
|
+
for i, segment in enumerate(route_segments):
|
|
53
|
+
if '{' in segment and '}' in segment: # parameter part
|
|
54
|
+
param_name = re.search(r'{(.*?)}', segment).group(1)
|
|
55
|
+
variables[param_name] = url_segments[i]
|
|
56
|
+
|
|
57
|
+
params = []
|
|
58
|
+
for variable in variables:
|
|
59
|
+
params.append({"name": variable, "in": "path", "type": "string"})
|
|
60
|
+
except Exception as e:
|
|
61
|
+
Debug.error("Failed to parse path inputs", str(e))
|
|
62
|
+
return []
|
|
63
|
+
|
|
64
|
+
return params
|
|
65
|
+
|
|
66
|
+
@staticmethod
|
|
67
|
+
def get_swagger_entry(url, method, tags, summary, description, produces, security, params=None, example=None,
|
|
68
|
+
responses=None):
|
|
69
|
+
|
|
70
|
+
if params is None:
|
|
71
|
+
params = []
|
|
72
|
+
|
|
73
|
+
schema = {}
|
|
74
|
+
if example is not None:
|
|
75
|
+
schema = {"type": "object", "example": example}
|
|
76
|
+
|
|
77
|
+
secure_annotation = [],
|
|
78
|
+
if security:
|
|
79
|
+
secure_annotation = [{"bearerAuth": []}]
|
|
80
|
+
|
|
81
|
+
new_params = []
|
|
82
|
+
for param in params:
|
|
83
|
+
param_value = param.split("=")
|
|
84
|
+
if len(param_value) < 2:
|
|
85
|
+
param_value.append("")
|
|
86
|
+
new_params.append({"name": param_value[0], "in": "query", "type": "string", "default": param_value[1]})
|
|
87
|
+
|
|
88
|
+
params = [*new_params, *Swagger.get_path_inputs(url)]
|
|
89
|
+
|
|
90
|
+
entry = {
|
|
91
|
+
"tags": tags,
|
|
92
|
+
"summary": summary,
|
|
93
|
+
"description": description,
|
|
94
|
+
"produces": produces,
|
|
95
|
+
"parameters": params,
|
|
96
|
+
"requestBody": {
|
|
97
|
+
"description": "Example Object",
|
|
98
|
+
"required": True,
|
|
99
|
+
"content": {
|
|
100
|
+
"application/json": {
|
|
101
|
+
"schema": schema
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
"security": secure_annotation,
|
|
106
|
+
"responses": responses
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if method == Constant.TINA4_GET or example is None:
|
|
110
|
+
del entry["requestBody"]
|
|
111
|
+
|
|
112
|
+
return entry
|
|
113
|
+
|
|
114
|
+
@staticmethod
|
|
115
|
+
def parse_swagger(swagger):
|
|
116
|
+
if "tags" not in swagger:
|
|
117
|
+
swagger["tags"] = []
|
|
118
|
+
if "params" not in swagger:
|
|
119
|
+
swagger["params"] = []
|
|
120
|
+
if "description" not in swagger:
|
|
121
|
+
swagger["description"] = ""
|
|
122
|
+
if "summary" not in swagger:
|
|
123
|
+
swagger["summary"] = ""
|
|
124
|
+
if "example" not in swagger:
|
|
125
|
+
swagger["example"] = None
|
|
126
|
+
if "secure" not in swagger:
|
|
127
|
+
swagger["secure"] = None
|
|
128
|
+
|
|
129
|
+
if isinstance(swagger["tags"], str):
|
|
130
|
+
swagger["tags"] = [swagger["tags"]]
|
|
131
|
+
|
|
132
|
+
return swagger
|
|
133
|
+
|
|
134
|
+
@staticmethod
|
|
135
|
+
def get_json(request):
|
|
136
|
+
paths = {}
|
|
137
|
+
for route in tina4_python.tina4_routes.values():
|
|
138
|
+
|
|
139
|
+
if "swagger" in route:
|
|
140
|
+
if route["swagger"] is not None:
|
|
141
|
+
swagger = Swagger.parse_swagger(route["swagger"])
|
|
142
|
+
responses = {
|
|
143
|
+
"200": {"description": "Success"},
|
|
144
|
+
"400": {"description": "Failed"}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if route["route"] not in paths:
|
|
148
|
+
paths[route["route"]] = {}
|
|
149
|
+
paths[route["route"]][route["method"].lower()] = Swagger.get_swagger_entry(route["route"],
|
|
150
|
+
route["method"].lower(),
|
|
151
|
+
swagger["tags"],
|
|
152
|
+
swagger["summary"],
|
|
153
|
+
swagger["description"],
|
|
154
|
+
["application/json",
|
|
155
|
+
"html/text"],
|
|
156
|
+
swagger["secure"],
|
|
157
|
+
swagger["params"],
|
|
158
|
+
swagger["example"],
|
|
159
|
+
responses)
|
|
160
|
+
|
|
161
|
+
if "host" in request.headers:
|
|
162
|
+
host_name = request.headers["host"]
|
|
163
|
+
else:
|
|
164
|
+
host_name = os.getenv("HOST_NAME", "localhost")
|
|
165
|
+
|
|
166
|
+
json_object = {
|
|
167
|
+
"openapi": "3.0.0",
|
|
168
|
+
"host": host_name,
|
|
169
|
+
"info": {
|
|
170
|
+
"title": os.getenv("SWAGGER_TITLE", "Tina4 Project(SWAGGER_TITLE)"),
|
|
171
|
+
"description": os.getenv("SWAGGER_DESCRIPTION", "Description(SWAGGER_DESCRIPTION)"),
|
|
172
|
+
"version": os.getenv("SWAGGER_VERSION", "1.0.0(SWAGGER_VERSION)")
|
|
173
|
+
},
|
|
174
|
+
"components": {
|
|
175
|
+
"securitySchemes": {"bearerAuth": {"type": "http", "scheme": "bearer", "bearerFormat": "JWT"}}},
|
|
176
|
+
"basePath": "",
|
|
177
|
+
"paths": paths
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return json_object
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def description(text):
|
|
184
|
+
def actual_description(callback):
|
|
185
|
+
Swagger.add_descripton(text, callback)
|
|
186
|
+
return callback
|
|
187
|
+
|
|
188
|
+
return actual_description
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def summary(text):
|
|
192
|
+
def actual_summary(callback):
|
|
193
|
+
Swagger.add_summary(text, callback)
|
|
194
|
+
return callback
|
|
195
|
+
|
|
196
|
+
return actual_summary
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def secure():
|
|
200
|
+
def actual_secure(callback):
|
|
201
|
+
Swagger.add_secure(callback)
|
|
202
|
+
return callback
|
|
203
|
+
|
|
204
|
+
return actual_secure
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def tags(tags):
|
|
208
|
+
def actual_tags(callback):
|
|
209
|
+
Swagger.add_tags(tags, callback)
|
|
210
|
+
return callback
|
|
211
|
+
|
|
212
|
+
return actual_tags
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def example(example):
|
|
216
|
+
def actual_example(callback):
|
|
217
|
+
Swagger.add_example(example, callback)
|
|
218
|
+
return callback
|
|
219
|
+
|
|
220
|
+
return actual_example
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def params(params):
|
|
224
|
+
def actual_params(callback):
|
|
225
|
+
Swagger.add_params(params, callback)
|
|
226
|
+
return callback
|
|
227
|
+
|
|
228
|
+
return actual_params
|
tina4_python/Template.py
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Tina4 - This is not a 4ramework.
|
|
3
|
+
# Copy-right 2007 - current Tina4
|
|
4
|
+
# License: MIT https://opensource.org/licenses/MIT
|
|
5
|
+
#
|
|
6
|
+
# flake8: noqa: E501
|
|
7
|
+
import os
|
|
8
|
+
import json
|
|
9
|
+
import tina4_python
|
|
10
|
+
from tina4_python import Constant
|
|
11
|
+
from tina4_python.Debug import Debug
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from datetime import datetime, date
|
|
14
|
+
from jinja2 import Environment, FileSystemLoader, Undefined
|
|
15
|
+
from tina4_python.Session import Session
|
|
16
|
+
from random import random as RANDOM
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Template:
|
|
20
|
+
# initializes the twig template engine
|
|
21
|
+
@staticmethod
|
|
22
|
+
def init_twig(path):
|
|
23
|
+
if hasattr(Template, "twig"):
|
|
24
|
+
Debug("Twig found on " + path, Constant.TINA4_LOG_DEBUG)
|
|
25
|
+
return Template.twig
|
|
26
|
+
Debug("Initializing Twig on " + path, Constant.TINA4_LOG_DEBUG)
|
|
27
|
+
twig_path = Path(path)
|
|
28
|
+
Template.twig = Environment(loader=FileSystemLoader(Path(twig_path)))
|
|
29
|
+
Template.twig.add_extension('jinja2.ext.debug')
|
|
30
|
+
Template.twig.add_extension('jinja2.ext.do')
|
|
31
|
+
Template.twig.globals['RANDOM'] = RANDOM
|
|
32
|
+
Template.twig.globals['formToken'] = Template.get_form_token
|
|
33
|
+
Template.twig.filters['formToken'] = Template.get_form_token_input
|
|
34
|
+
if Constant.TINA4_LOG_DEBUG in os.getenv("TINA4_DEBUG_LEVEL") or Constant.TINA4_LOG_ALL in os.getenv(
|
|
35
|
+
"TINA4_DEBUG_LEVEL"):
|
|
36
|
+
Template.twig.globals['dump'] = Template.dump
|
|
37
|
+
else:
|
|
38
|
+
Template.twig.globals['dump'] = Template.production_dump
|
|
39
|
+
Debug("Twig Initialized on " + path, Constant.TINA4_LOG_INFO)
|
|
40
|
+
return Template.twig
|
|
41
|
+
|
|
42
|
+
@staticmethod
|
|
43
|
+
def production_dump(param):
|
|
44
|
+
Debug.error("DUMP FOUND ON PAGE!")
|
|
45
|
+
return ""
|
|
46
|
+
|
|
47
|
+
@staticmethod
|
|
48
|
+
def dump(param):
|
|
49
|
+
if param is not None and not isinstance(param, Undefined):
|
|
50
|
+
def json_serialize(obj):
|
|
51
|
+
if isinstance(obj, (date, datetime)):
|
|
52
|
+
return obj.isoformat()
|
|
53
|
+
if isinstance(obj, Session):
|
|
54
|
+
return obj.session_values
|
|
55
|
+
raise TypeError("Type %s not serializable to Jinja2 template" % type(obj))
|
|
56
|
+
|
|
57
|
+
return "<pre>" + json.dumps(param, indent=True, default=json_serialize) + "</pre>"
|
|
58
|
+
else:
|
|
59
|
+
return ""
|
|
60
|
+
|
|
61
|
+
@staticmethod
|
|
62
|
+
def get_form_token(payload={}):
|
|
63
|
+
return tina4_python.tina4_auth.get_token(payload)
|
|
64
|
+
|
|
65
|
+
@staticmethod
|
|
66
|
+
def get_form_token_input(form_name):
|
|
67
|
+
return '<input type="hidden" name="formToken" value="' + Template.get_form_token(
|
|
68
|
+
{"formName": form_name}) + '"><!--"' + str(datetime.now().isoformat()) + '"-->'
|
|
69
|
+
|
|
70
|
+
@staticmethod
|
|
71
|
+
def convert_special_types(obj):
|
|
72
|
+
if isinstance(obj, dict):
|
|
73
|
+
return {k: Template.convert_special_types(v) for k, v in obj.items()}
|
|
74
|
+
elif isinstance(obj, list):
|
|
75
|
+
return [Template.convert_special_types(i) for i in obj]
|
|
76
|
+
elif isinstance(obj, (date, datetime)):
|
|
77
|
+
return obj.isoformat()
|
|
78
|
+
else:
|
|
79
|
+
return obj
|
|
80
|
+
|
|
81
|
+
@staticmethod
|
|
82
|
+
def render_twig_template(template_or_file_name, data=None):
|
|
83
|
+
if data is None:
|
|
84
|
+
data = {"request": tina4_python.tina4_current_request}
|
|
85
|
+
else:
|
|
86
|
+
data.update({"request": tina4_python.tina4_current_request})
|
|
87
|
+
|
|
88
|
+
data = Template.convert_special_types(data)
|
|
89
|
+
|
|
90
|
+
twig = Template.init_twig(tina4_python.root_path + os.sep + "src" + os.sep + "templates")
|
|
91
|
+
try:
|
|
92
|
+
if twig.get_template(template_or_file_name):
|
|
93
|
+
template = twig.get_template(template_or_file_name)
|
|
94
|
+
content = template.render(data)
|
|
95
|
+
else:
|
|
96
|
+
template = twig.from_string(template_or_file_name)
|
|
97
|
+
content = template.render(data)
|
|
98
|
+
|
|
99
|
+
except Exception as e:
|
|
100
|
+
Debug("Error rendering twig file", template_or_file_name, e, Constant.TINA4_LOG_ERROR)
|
|
101
|
+
content = str(e)
|
|
102
|
+
|
|
103
|
+
return content
|
|
104
|
+
|
|
105
|
+
@staticmethod
|
|
106
|
+
def render(template_or_file_name, data=None):
|
|
107
|
+
return Template.render_twig_template(template_or_file_name, data)
|