liberty-framework 6.0.45__py3-none-any.whl → 6.0.46__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.
- app/controllers/__pycache__/api_controller.cpython-312.pyc +0 -0
- app/controllers/__pycache__/setup_controller.cpython-312.pyc +0 -0
- app/controllers/api_controller.py +3 -1
- app/controllers/setup_controller.py +7 -1
- app/logs/files/logs-frontend-json.log +3 -0
- app/logs/files/logs-frontend-text.log +12 -0
- app/models/__pycache__/setup.cpython-312.pyc +0 -0
- app/models/setup.py +28 -1
- app/routes/__pycache__/api_routes.cpython-312.pyc +0 -0
- app/routes/__pycache__/setup_routes.cpython-312.pyc +0 -0
- app/routes/api_routes.py +63 -1
- app/routes/setup_routes.py +40 -2
- app/services/__pycache__/api_rest.cpython-312.pyc +0 -0
- app/services/api_rest.py +96 -5
- app/setup/services/__pycache__/install.cpython-312.pyc +0 -0
- app/setup/services/__pycache__/setup.cpython-312.pyc +0 -0
- app/setup/services/install.py +4 -0
- app/setup/services/setup.py +141 -8
- {liberty_framework-6.0.45.dist-info → liberty_framework-6.0.46.dist-info}/METADATA +4 -1
- {liberty_framework-6.0.45.dist-info → liberty_framework-6.0.46.dist-info}/RECORD +24 -24
- {liberty_framework-6.0.45.dist-info → liberty_framework-6.0.46.dist-info}/WHEEL +1 -1
- {liberty_framework-6.0.45.dist-info → liberty_framework-6.0.46.dist-info}/LICENSE +0 -0
- {liberty_framework-6.0.45.dist-info → liberty_framework-6.0.46.dist-info}/entry_points.txt +0 -0
- {liberty_framework-6.0.45.dist-info → liberty_framework-6.0.46.dist-info}/top_level.txt +0 -0
|
Binary file
|
|
Binary file
|
|
@@ -37,4 +37,10 @@ class SetupController:
|
|
|
37
37
|
return self.alembic.revision(req)
|
|
38
38
|
|
|
39
39
|
def current(self, req: Request):
|
|
40
|
-
return self.alembic.current(req)
|
|
40
|
+
return self.alembic.current(req)
|
|
41
|
+
|
|
42
|
+
async def create(self, req: Request):
|
|
43
|
+
return await self.setupRest.create_database(req)
|
|
44
|
+
|
|
45
|
+
async def drop(self, req: Request):
|
|
46
|
+
return await self.setupRest.drop_database(req)
|
|
@@ -201,3 +201,6 @@
|
|
|
201
201
|
{"timestamp": "2025-02-20T09:15:08.302464+00:00", "transactionName": "QueryDAO.applications", "level": "info", "method": "GET", "url": "http://localhost:5173/api/fmw/applications", "data": {"message": "Request failed with status code 500: undefined"}, "message": "Framework: Load Applications", "category": "http", "feature": "database-api", "isException": true}
|
|
202
202
|
{"timestamp": "2025-02-20T09:15:08.303872+00:00", "transactionName": "loginUtils.getApplications", "level": "info", "method": null, "url": null, "data": {"status": "error", "items": [{"message": "Request failed with status code 500: undefined"}]}, "message": "Login: Failed to fetch applications", "category": "debug", "feature": "console", "isException": true}
|
|
203
203
|
{"timestamp": "2025-02-20T09:20:53.819597+00:00", "transactionName": "QueryDAO.login", "level": "info", "method": "GET", "url": "http://localhost:5173/api/auth/user?user=admin&pool=libnsx1&mode=session", "data": {"message": "Request failed with status code 500: Error creating pool: Error creating pool: [Errno 8] nodename nor servname provided, or not known"}, "message": "User Login: Get User Properties", "category": "http", "feature": "database-api", "isException": true}
|
|
204
|
+
{"timestamp": "2025-02-26T15:16:17.684769+00:00", "transactionName": "APIDAO.post", "level": "info", "method": "POST", "url": "http://localhost:5173/api/rest?pool=default&mode=session&api=1", "data": null, "message": "REST: Failed POST API call", "category": "http", "feature": "rest-api", "isException": true}
|
|
205
|
+
{"timestamp": "2025-02-26T16:00:40.041233+00:00", "transactionName": "APIDAO.post", "level": "info", "method": "POST", "url": "http://localhost:5173/api/rest?pool=default&mode=session&api=1", "data": null, "message": "REST: Failed POST API call", "category": "http", "feature": "rest-api", "isException": true}
|
|
206
|
+
{"timestamp": "2025-02-26T16:27:09.583000+00:00", "transactionName": "APIDAO.post", "level": "info", "method": "POST", "url": "http://localhost:5173/api/rest?pool=default&mode=session&api=1", "data": null, "message": "REST: Failed POST API call", "category": "http", "feature": "rest-api", "isException": true}
|
|
@@ -810,3 +810,15 @@ Category: debug, Feature: console, IsException: True
|
|
|
810
810
|
Method: GET, URL: http://localhost:5173/api/auth/user?user=admin&pool=libnsx1&mode=session
|
|
811
811
|
Category: http, Feature: database-api, IsException: True
|
|
812
812
|
|
|
813
|
+
[2025-02-26T15:16:17.684769+00:00] [info] APIDAO.post - REST: Failed POST API call
|
|
814
|
+
Method: POST, URL: http://localhost:5173/api/rest?pool=default&mode=session&api=1
|
|
815
|
+
Category: http, Feature: rest-api, IsException: True
|
|
816
|
+
|
|
817
|
+
[2025-02-26T16:00:40.041233+00:00] [info] APIDAO.post - REST: Failed POST API call
|
|
818
|
+
Method: POST, URL: http://localhost:5173/api/rest?pool=default&mode=session&api=1
|
|
819
|
+
Category: http, Feature: rest-api, IsException: True
|
|
820
|
+
|
|
821
|
+
[2025-02-26T16:27:09.583000+00:00] [info] APIDAO.post - REST: Failed POST API call
|
|
822
|
+
Method: POST, URL: http://localhost:5173/api/rest?pool=default&mode=session&api=1
|
|
823
|
+
Category: http, Feature: rest-api, IsException: True
|
|
824
|
+
|
|
Binary file
|
app/models/setup.py
CHANGED
|
@@ -14,4 +14,31 @@ SETUP_RESPONSE_EXAMPLE = {
|
|
|
14
14
|
"items": [],
|
|
15
15
|
"status": "success",
|
|
16
16
|
"count": 0
|
|
17
|
-
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
class CreateRequest(BaseModel):
|
|
20
|
+
host: str
|
|
21
|
+
port: int
|
|
22
|
+
database: str
|
|
23
|
+
user: str
|
|
24
|
+
password: str
|
|
25
|
+
|
|
26
|
+
CREATE_ERROR_MESSAGE = "Create database failed"
|
|
27
|
+
CREATE_RESPONSE_DESCRIPTION = "Create database successful"
|
|
28
|
+
CREATE_RESPONSE_EXAMPLE = {
|
|
29
|
+
"items": [],
|
|
30
|
+
"status": "success",
|
|
31
|
+
"count": 0
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
class DropRequest(BaseModel):
|
|
35
|
+
database: str
|
|
36
|
+
user: str
|
|
37
|
+
|
|
38
|
+
DROP_ERROR_MESSAGE = "Drop database failed"
|
|
39
|
+
DROP_RESPONSE_DESCRIPTION = "Drop Database successful"
|
|
40
|
+
DROP_RESPONSE_EXAMPLE = {
|
|
41
|
+
"items": [],
|
|
42
|
+
"status": "success",
|
|
43
|
+
"count": 0
|
|
44
|
+
}
|
|
Binary file
|
|
Binary file
|
app/routes/api_routes.py
CHANGED
|
@@ -230,7 +230,7 @@ def setup_api_routes(app, controller: ApiController, jwt: JWT):
|
|
|
230
230
|
):
|
|
231
231
|
return await controller.close(req)
|
|
232
232
|
|
|
233
|
-
|
|
233
|
+
|
|
234
234
|
@router.get(
|
|
235
235
|
"/db/query",
|
|
236
236
|
response_model=GetSuccessResponse, # Specify the success response schema
|
|
@@ -426,4 +426,66 @@ def setup_api_routes(app, controller: ApiController, jwt: JWT):
|
|
|
426
426
|
return await controller.ai_welcome(req)
|
|
427
427
|
|
|
428
428
|
|
|
429
|
+
@router.post(
|
|
430
|
+
"/rest",
|
|
431
|
+
response_model=PostSuccessResponse, # Specify the success response schema
|
|
432
|
+
responses={
|
|
433
|
+
200: response_200(PostSuccessResponse, POST_APIDB_RESPONSE_DESCRIPTION, POST_APIDB_RESPONSE_EXAMPLE),
|
|
434
|
+
400: response_400("Request body cannot be empty."),
|
|
435
|
+
422: response_422(),
|
|
436
|
+
500: response_500(PostErrorResponse, POST_APIDB_ERROR_EXAMPLE),
|
|
437
|
+
},
|
|
438
|
+
summary="REST - Call Post Api",
|
|
439
|
+
description="Call a rest api (post).",
|
|
440
|
+
tags=["Query"],
|
|
441
|
+
)
|
|
442
|
+
async def post(
|
|
443
|
+
req: Request,
|
|
444
|
+
jwt: str = Depends(jwt.is_valid_jwt),
|
|
445
|
+
source: QuerySource = Query(None, description="The source to retrieve the query definition. Valid values: `framework`, `query`"),
|
|
446
|
+
type: QueryType = Query(None, description="The type of query, get data or metadata. Valid values: `table`, `columns`."),
|
|
447
|
+
pool: str = Query(None, description="The database pool alias to retrieve the query definition. (e.g., `default`, `libnsx1`)"),
|
|
448
|
+
mode: SessionMode = Query(None, description="The session mode, retrieve data from framework table or pool. Valid values: `framework`, `session`"),
|
|
449
|
+
query: int = Query(None, description="The query ID to execute. (e.g., `1`, `2`)"),
|
|
450
|
+
override_pool: Optional[str] = Query(None, description="Override the default pool set in the query definition. (e.g., `default`, `libnsx1`)"),
|
|
451
|
+
body: Dict[str, Any] = Body(..., description="JSON object with key-value pairs is required.")
|
|
452
|
+
):
|
|
453
|
+
if not body: # Check if the body is empty
|
|
454
|
+
raise HTTPException(
|
|
455
|
+
status_code=400,
|
|
456
|
+
detail="Request body cannot be empty. JSON object with key-value pairs is required.",
|
|
457
|
+
)
|
|
458
|
+
return await controller.rest(req)
|
|
459
|
+
|
|
460
|
+
@router.get(
|
|
461
|
+
"/rest",
|
|
462
|
+
response_model=PostSuccessResponse, # Specify the success response schema
|
|
463
|
+
responses={
|
|
464
|
+
200: response_200(PostSuccessResponse, POST_APIDB_RESPONSE_DESCRIPTION, POST_APIDB_RESPONSE_EXAMPLE),
|
|
465
|
+
400: response_400("Request body cannot be empty."),
|
|
466
|
+
422: response_422(),
|
|
467
|
+
500: response_500(PostErrorResponse, POST_APIDB_ERROR_EXAMPLE),
|
|
468
|
+
},
|
|
469
|
+
summary="REST - Call Post Api",
|
|
470
|
+
description="Call a rest api (post).",
|
|
471
|
+
tags=["Query"],
|
|
472
|
+
)
|
|
473
|
+
async def get(
|
|
474
|
+
req: Request,
|
|
475
|
+
jwt: str = Depends(jwt.is_valid_jwt),
|
|
476
|
+
source: QuerySource = Query(None, description="The source to retrieve the query definition. Valid values: `framework`, `query`"),
|
|
477
|
+
type: QueryType = Query(None, description="The type of query, get data or metadata. Valid values: `table`, `columns`."),
|
|
478
|
+
pool: str = Query(None, description="The database pool alias to retrieve the query definition. (e.g., `default`, `libnsx1`)"),
|
|
479
|
+
mode: SessionMode = Query(None, description="The session mode, retrieve data from framework table or pool. Valid values: `framework`, `session`"),
|
|
480
|
+
query: int = Query(None, description="The query ID to execute. (e.g., `1`, `2`)"),
|
|
481
|
+
override_pool: Optional[str] = Query(None, description="Override the default pool set in the query definition. (e.g., `default`, `libnsx1`)"),
|
|
482
|
+
body: Dict[str, Any] = Body(..., description="JSON object with key-value pairs is required.")
|
|
483
|
+
):
|
|
484
|
+
if not body: # Check if the body is empty
|
|
485
|
+
raise HTTPException(
|
|
486
|
+
status_code=400,
|
|
487
|
+
detail="Request body cannot be empty. JSON object with key-value pairs is required.",
|
|
488
|
+
)
|
|
489
|
+
return await controller.rest(req)
|
|
490
|
+
|
|
429
491
|
app.include_router(router, prefix="/api")
|
app/routes/setup_routes.py
CHANGED
|
@@ -8,7 +8,7 @@ from fastapi import APIRouter, Request
|
|
|
8
8
|
|
|
9
9
|
from app.controllers.setup_controller import SetupController
|
|
10
10
|
from app.models.base import ErrorResponse, SuccessResponse, response_200, response_422, response_500
|
|
11
|
-
from app.models.setup import SETUP_ERROR_MESSAGE, SETUP_RESPONSE_DESCRIPTION, SETUP_RESPONSE_EXAMPLE, SetupRequest
|
|
11
|
+
from app.models.setup import CREATE_ERROR_MESSAGE, CREATE_RESPONSE_DESCRIPTION, CREATE_RESPONSE_EXAMPLE, DROP_ERROR_MESSAGE, DROP_RESPONSE_DESCRIPTION, DROP_RESPONSE_EXAMPLE, SETUP_ERROR_MESSAGE, SETUP_RESPONSE_DESCRIPTION, SETUP_RESPONSE_EXAMPLE, CreateRequest, DropRequest, SetupRequest
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
def setup_setup_routes(app, controller: SetupController):
|
|
@@ -205,4 +205,42 @@ def setup_setup_routes(app, controller: SetupController):
|
|
|
205
205
|
):
|
|
206
206
|
return controller.current(req)
|
|
207
207
|
|
|
208
|
-
|
|
208
|
+
|
|
209
|
+
@router.post(
|
|
210
|
+
"/db/create",
|
|
211
|
+
summary="DATABASE - Create",
|
|
212
|
+
description="Create database for new application",
|
|
213
|
+
tags=["Database"],
|
|
214
|
+
response_model=SuccessResponse,
|
|
215
|
+
responses={
|
|
216
|
+
200: response_200(SuccessResponse, CREATE_RESPONSE_DESCRIPTION, CREATE_RESPONSE_EXAMPLE),
|
|
217
|
+
422: response_422(),
|
|
218
|
+
500: response_500(ErrorResponse, CREATE_ERROR_MESSAGE),
|
|
219
|
+
},
|
|
220
|
+
)
|
|
221
|
+
async def create(
|
|
222
|
+
req: Request,
|
|
223
|
+
body: CreateRequest,
|
|
224
|
+
):
|
|
225
|
+
return await controller.create(req)
|
|
226
|
+
|
|
227
|
+
@router.post(
|
|
228
|
+
"/db/drop",
|
|
229
|
+
summary="DATABASE - Drop",
|
|
230
|
+
description="Drop an existing database",
|
|
231
|
+
tags=["Database"],
|
|
232
|
+
response_model=SuccessResponse,
|
|
233
|
+
responses={
|
|
234
|
+
200: response_200(SuccessResponse, DROP_RESPONSE_EXAMPLE, DROP_RESPONSE_DESCRIPTION),
|
|
235
|
+
422: response_422(),
|
|
236
|
+
500: response_500(ErrorResponse, DROP_ERROR_MESSAGE),
|
|
237
|
+
},
|
|
238
|
+
)
|
|
239
|
+
async def drop(
|
|
240
|
+
req: Request,
|
|
241
|
+
body: DropRequest,
|
|
242
|
+
):
|
|
243
|
+
return await controller.drop(req)
|
|
244
|
+
|
|
245
|
+
app.include_router(router, prefix="/api")
|
|
246
|
+
|
|
Binary file
|
app/services/api_rest.py
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
# Description: API REST service for handling REST API requests.
|
|
2
2
|
import logging
|
|
3
3
|
import os
|
|
4
|
+
import re
|
|
5
|
+
from urllib.parse import urljoin, urlparse
|
|
4
6
|
|
|
7
|
+
from fastapi.responses import JSONResponse
|
|
5
8
|
import httpx
|
|
6
9
|
from pydantic import BaseModel
|
|
7
10
|
|
|
8
|
-
from app.services.db_query import Query
|
|
11
|
+
from app.services.db_query import Query, SessionMode
|
|
12
|
+
from app.utils.encrypt import Encryption
|
|
9
13
|
logger = logging.getLogger(__name__)
|
|
10
14
|
|
|
11
15
|
import json
|
|
@@ -14,14 +18,12 @@ from datetime import datetime, timezone
|
|
|
14
18
|
from app.utils.logs import LogHandler
|
|
15
19
|
from app.logs import get_logs_json_path, get_logs_text_path
|
|
16
20
|
|
|
21
|
+
defaultPool = "default"
|
|
22
|
+
|
|
17
23
|
class ApiType:
|
|
18
24
|
internal = "INTERNAL"
|
|
19
25
|
external = "EXTERNAL"
|
|
20
26
|
|
|
21
|
-
class ApiFramework:
|
|
22
|
-
CreateFrameworkDatabase = "CreateFrameworkDatabase"
|
|
23
|
-
DropFrameworkDatabase = "DropFrameworkDatabase"
|
|
24
|
-
|
|
25
27
|
class AIResponse(BaseModel):
|
|
26
28
|
message: str
|
|
27
29
|
is_truncated: bool
|
|
@@ -32,6 +34,95 @@ class Rest:
|
|
|
32
34
|
self.logs_handler = LogHandler()
|
|
33
35
|
self.queryRest = queryRest
|
|
34
36
|
|
|
37
|
+
async def rest(self, req: Request):
|
|
38
|
+
try:
|
|
39
|
+
query_params = req.query_params
|
|
40
|
+
|
|
41
|
+
"""Extracts OpenAI API URL and Key from MODULE_ID = 'AI'."""
|
|
42
|
+
query = {
|
|
43
|
+
"QUERY": 34,
|
|
44
|
+
"POOL": query_params.get("mode") == SessionMode.framework and defaultPool or query_params.get("pool"),
|
|
45
|
+
"CRUD": "GET",
|
|
46
|
+
}
|
|
47
|
+
context = {
|
|
48
|
+
"row_offset": 0,
|
|
49
|
+
"row_limit": 1000,
|
|
50
|
+
"where": {"API_ID":query_params.get("api")},
|
|
51
|
+
}
|
|
52
|
+
# Get the target query using the framework query method
|
|
53
|
+
target_query = await self.queryRest.db_pools.get_pool("default").db_dao.get_framework_query(
|
|
54
|
+
query, self.queryRest.db_pools.get_pool("default").db_type
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
api = await self.queryRest.db_pools.get_pool("default").db_dao.get(target_query, context)
|
|
58
|
+
rows = api.get("rows")
|
|
59
|
+
|
|
60
|
+
if not api.get("rows"):
|
|
61
|
+
raise ValueError("No API found")
|
|
62
|
+
|
|
63
|
+
row = rows[0] # Extract the first row (dictionary)
|
|
64
|
+
|
|
65
|
+
api_type = row.get("API_SOURCE")
|
|
66
|
+
method = row.get("API_METHOD")
|
|
67
|
+
url = row.get("API_URL")
|
|
68
|
+
user = row.get("API_USER")
|
|
69
|
+
password = row.get("API_PASSWORD")
|
|
70
|
+
body = row.get("API_BODY")
|
|
71
|
+
|
|
72
|
+
# Convert API_BODY from JSON string format to a Python dictionary
|
|
73
|
+
body_dict = json.loads(body)
|
|
74
|
+
|
|
75
|
+
# 🔹 Ensure request body is retrieved properly
|
|
76
|
+
req_body = await req.json()
|
|
77
|
+
|
|
78
|
+
# Perform variable substitution in body
|
|
79
|
+
body_str = json.dumps(body_dict) # Convert dictionary to string for replacement
|
|
80
|
+
for key, value in req_body.items():
|
|
81
|
+
variable = rf"\${key.upper()}"
|
|
82
|
+
body_str = re.sub(variable, str(value), body_str)
|
|
83
|
+
|
|
84
|
+
# Convert back to dictionary
|
|
85
|
+
parsed_body = json.loads(body_str)
|
|
86
|
+
if api_type == ApiType.internal:
|
|
87
|
+
base_url = str(req.base_url)
|
|
88
|
+
full_url = urljoin(base_url, url)
|
|
89
|
+
else:
|
|
90
|
+
# Check if `url` is already a full external URL
|
|
91
|
+
parsed_url = urlparse(url)
|
|
92
|
+
if parsed_url.scheme and parsed_url.netloc:
|
|
93
|
+
full_url = url # Use the full external URL as is
|
|
94
|
+
else:
|
|
95
|
+
raise ValueError(f"Invalid external URL: {url}")
|
|
96
|
+
|
|
97
|
+
# 🔹 Make the API call
|
|
98
|
+
async with httpx.AsyncClient(timeout=60.0) as client:
|
|
99
|
+
response = await client.post(full_url, json=parsed_body)
|
|
100
|
+
if response.status_code == 200:
|
|
101
|
+
response_data = response.json()
|
|
102
|
+
else:
|
|
103
|
+
response_data = {
|
|
104
|
+
"error": f"Failed request with status code {response.status_code}",
|
|
105
|
+
"details": response.text
|
|
106
|
+
}
|
|
107
|
+
response_data = response.json()
|
|
108
|
+
|
|
109
|
+
return JSONResponse({
|
|
110
|
+
"items": response_data,
|
|
111
|
+
"status": "success",
|
|
112
|
+
"count": 0,
|
|
113
|
+
})
|
|
114
|
+
except Exception as err:
|
|
115
|
+
message = str(err)
|
|
116
|
+
return JSONResponse({
|
|
117
|
+
"items": [{"message": f"Error: {message}"}],
|
|
118
|
+
"status": "error",
|
|
119
|
+
"hasMore": False,
|
|
120
|
+
"limit": context.get("row_limit", 1000),
|
|
121
|
+
"offset": context.get("row_offset", 0),
|
|
122
|
+
"count": 0,
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
|
|
35
126
|
|
|
36
127
|
async def push_log(self, req: Request):
|
|
37
128
|
"""
|
|
Binary file
|
|
Binary file
|
app/setup/services/install.py
CHANGED
app/setup/services/setup.py
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import logging
|
|
2
|
+
|
|
3
|
+
from app.setup.models.liberty import Base
|
|
2
4
|
logger = logging.getLogger(__name__)
|
|
3
5
|
|
|
4
6
|
from sqlalchemy import create_engine, text
|
|
@@ -35,13 +37,6 @@ class Setup:
|
|
|
35
37
|
admin_password = data.get("admin_password")
|
|
36
38
|
load_data = data.get("load_data", False)
|
|
37
39
|
|
|
38
|
-
# Create all tables in the database
|
|
39
|
-
# for table in Base.metadata.tables.values():
|
|
40
|
-
# if not table.schema:
|
|
41
|
-
# table.schema = database # 🔹 Assign schema to tables
|
|
42
|
-
# Base.metadata.create_all(engine)
|
|
43
|
-
# logging.warning("All tables have been successfully created!")
|
|
44
|
-
|
|
45
40
|
# Database configuration
|
|
46
41
|
ADMIN_DATABASE_URL = f"postgresql+psycopg2://{user}:{current_password}@{host}:{port}/{admin_database}"
|
|
47
42
|
|
|
@@ -423,4 +418,142 @@ pool_alias=default
|
|
|
423
418
|
"items": [{"message": f"Error: {message}"}],
|
|
424
419
|
"status": "error",
|
|
425
420
|
"count": 0
|
|
426
|
-
})
|
|
421
|
+
})
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
async def create_database(self, req: Request):
|
|
425
|
+
try:
|
|
426
|
+
data = await req.json()
|
|
427
|
+
host = data.get("host")
|
|
428
|
+
port = data.get("port")
|
|
429
|
+
database = data.get("database")
|
|
430
|
+
user = data.get("user")
|
|
431
|
+
password = data.get("password")
|
|
432
|
+
|
|
433
|
+
# Database configuration
|
|
434
|
+
db_properties_path = get_db_properties_path()
|
|
435
|
+
config = self.apiController.queryRest.load_db_properties(db_properties_path)
|
|
436
|
+
# Database configuration
|
|
437
|
+
ADMIN_DATABASE_URL = f"postgresql+psycopg2://{config["user"]}:{config["password"]}@{config["host"]}:{config["port"]}/{config["database"]}"
|
|
438
|
+
|
|
439
|
+
# Create an engine
|
|
440
|
+
admin_engine = create_engine(ADMIN_DATABASE_URL, isolation_level="AUTOCOMMIT")
|
|
441
|
+
with admin_engine.connect() as conn:
|
|
442
|
+
result = conn.execute(text(f"SELECT 1 FROM pg_database WHERE datname = '{database}'"))
|
|
443
|
+
db_exists = result.scalar()
|
|
444
|
+
|
|
445
|
+
if not db_exists:
|
|
446
|
+
logging.warning(f"Creating database '{database}'...")
|
|
447
|
+
conn.execute(text(f'CREATE DATABASE "{database}"'))
|
|
448
|
+
else:
|
|
449
|
+
logging.warning(f"Database '{database}' already exists. Skipping creation.")
|
|
450
|
+
# 🚀 Check if the role exists
|
|
451
|
+
result = conn.execute(text(f"SELECT 1 FROM pg_roles WHERE rolname = '{user}'"))
|
|
452
|
+
role_exists = result.scalar()
|
|
453
|
+
|
|
454
|
+
if not role_exists:
|
|
455
|
+
logging.warning(f"Creating role '{user}' with password...")
|
|
456
|
+
conn.execute(text(f"CREATE ROLE {user} WITH LOGIN PASSWORD '{password}'"))
|
|
457
|
+
else:
|
|
458
|
+
logging.warning(f"Role '{user}' already exists. Skipping creation.")
|
|
459
|
+
|
|
460
|
+
# 🚀 Grant privileges to the role
|
|
461
|
+
conn.execute(text(f'GRANT ALL PRIVILEGES ON DATABASE "{database}" TO {user}'))
|
|
462
|
+
logging.warning(f"Granted privileges to role '{user}' on database '{database}'.")
|
|
463
|
+
|
|
464
|
+
# Create all tables in the database
|
|
465
|
+
DATABASE_URL = f"postgresql+psycopg2://{user}:{password}@{host}:{port}/{database}"
|
|
466
|
+
engine = create_engine(DATABASE_URL, echo=False, isolation_level="AUTOCOMMIT")
|
|
467
|
+
|
|
468
|
+
with engine.connect() as conn:
|
|
469
|
+
result = conn.execute(text(f"SELECT 1 FROM information_schema.schemata WHERE schema_name = '{database}'"))
|
|
470
|
+
schema_exists = result.scalar()
|
|
471
|
+
|
|
472
|
+
if not schema_exists:
|
|
473
|
+
logging.warning(f"Creating schema '{user}'...")
|
|
474
|
+
conn.execute(text(f'CREATE SCHEMA "{user}" AUTHORIZATION {user}'))
|
|
475
|
+
else:
|
|
476
|
+
logging.warning(f"Schema '{user}' already exists. Skipping creation.")
|
|
477
|
+
|
|
478
|
+
for table in Base.metadata.tables.values():
|
|
479
|
+
if not table.schema:
|
|
480
|
+
table.schema = database
|
|
481
|
+
Base.metadata.create_all(engine)
|
|
482
|
+
|
|
483
|
+
logging.warning("All tables have been successfully created!")
|
|
484
|
+
# Return the response
|
|
485
|
+
return JSONResponse({
|
|
486
|
+
"items": [],
|
|
487
|
+
"status": "success",
|
|
488
|
+
"count": 0
|
|
489
|
+
})
|
|
490
|
+
|
|
491
|
+
except Exception as err:
|
|
492
|
+
message = str(err)
|
|
493
|
+
return JSONResponse({
|
|
494
|
+
"items": [{"message": f"Error: {message}"}],
|
|
495
|
+
"status": "error",
|
|
496
|
+
"count": 0
|
|
497
|
+
})
|
|
498
|
+
|
|
499
|
+
async def drop_database(self, req: Request):
|
|
500
|
+
try:
|
|
501
|
+
data = await req.json()
|
|
502
|
+
database = data.get("database")
|
|
503
|
+
user = data.get("user")
|
|
504
|
+
|
|
505
|
+
# Database configuration
|
|
506
|
+
db_properties_path = get_db_properties_path()
|
|
507
|
+
config = self.apiController.queryRest.load_db_properties(db_properties_path)
|
|
508
|
+
# Database configuration
|
|
509
|
+
ADMIN_DATABASE_URL = f"postgresql+psycopg2://{config["user"]}:{config["password"]}@{config["host"]}:{config["port"]}/{config["database"]}"
|
|
510
|
+
|
|
511
|
+
# Create an engine
|
|
512
|
+
admin_engine = create_engine(ADMIN_DATABASE_URL, isolation_level="AUTOCOMMIT")
|
|
513
|
+
with admin_engine.connect() as conn:
|
|
514
|
+
# 🚀 Check if database exists
|
|
515
|
+
result = conn.execute(text(f"SELECT 1 FROM pg_database WHERE datname = '{database}'"))
|
|
516
|
+
db_exists = result.scalar()
|
|
517
|
+
|
|
518
|
+
if db_exists:
|
|
519
|
+
logging.warning(f"Revoking new connections to database '{database}'...")
|
|
520
|
+
conn.execute(text(f"UPDATE pg_database SET datallowconn = FALSE WHERE datname = '{database}'"))
|
|
521
|
+
|
|
522
|
+
logging.warning(f"Terminating active connections to database '{database}'...")
|
|
523
|
+
conn.execute(text(f"""
|
|
524
|
+
SELECT pg_terminate_backend(pg_stat_activity.pid)
|
|
525
|
+
FROM pg_stat_activity
|
|
526
|
+
WHERE pg_stat_activity.datname = '{database}'
|
|
527
|
+
AND pid <> pg_backend_pid();
|
|
528
|
+
"""))
|
|
529
|
+
|
|
530
|
+
logging.warning(f"Dropping database '{database}'...")
|
|
531
|
+
conn.execute(text(f'DROP DATABASE "{database}"'))
|
|
532
|
+
else:
|
|
533
|
+
logging.warning(f"Database '{database}' does not exist. Skipping drop.")
|
|
534
|
+
|
|
535
|
+
# 🚀 Check if the role exists
|
|
536
|
+
result = conn.execute(text(f"SELECT 1 FROM pg_roles WHERE rolname = '{user}'"))
|
|
537
|
+
role_exists = result.scalar()
|
|
538
|
+
|
|
539
|
+
if role_exists:
|
|
540
|
+
logging.warning(f"Dropping role '{user}'...")
|
|
541
|
+
conn.execute(text(f"DROP ROLE {user}"))
|
|
542
|
+
else:
|
|
543
|
+
logging.warning(f"Role '{user}' does not exist. Skipping drop.")
|
|
544
|
+
|
|
545
|
+
logging.warning("Database successfully dropped!")
|
|
546
|
+
# Return the response
|
|
547
|
+
return JSONResponse({
|
|
548
|
+
"items": [],
|
|
549
|
+
"status": "success",
|
|
550
|
+
"count": 0
|
|
551
|
+
})
|
|
552
|
+
|
|
553
|
+
except Exception as err:
|
|
554
|
+
message = str(err)
|
|
555
|
+
return JSONResponse({
|
|
556
|
+
"items": [{"message": f"Error: {message}"}],
|
|
557
|
+
"status": "error",
|
|
558
|
+
"count": 0
|
|
559
|
+
})
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: liberty-framework
|
|
3
|
-
Version: 6.0.
|
|
3
|
+
Version: 6.0.46
|
|
4
4
|
Summary: Liberty Framework
|
|
5
5
|
Author: Franck Blettner
|
|
6
6
|
Author-email: franck.blettner@nomana-it.fr
|
|
@@ -84,6 +84,9 @@ Dynamic: summary
|
|
|
84
84
|
# 📖 Liberty Framework
|
|
85
85
|
### A Scalable and Extensible FastAPI and React Framework for Business Applications
|
|
86
86
|
|
|
87
|
+
## Announcements
|
|
88
|
+
- **Release 6.0.46**: Implement call for custom rest api, add drop and create database for framework
|
|
89
|
+
|
|
87
90
|
🚀 **Liberty Framework** is a powerful, modular, and extensible **FastAPI-based and React-based framework** designed to streamline backend development for business applications. It provides **database management, authentication, real-time socket communication, and more**, making it easy to deploy and scale enterprise solutions.
|
|
88
91
|
|
|
89
92
|
- Online demo is available at [https://liberty.nomana-it.fr](https://liberty.nomana-it.fr)
|
|
@@ -30,13 +30,13 @@ app/config/__pycache__/__init__.cpython-312.pyc,sha256=sHDvTIPFsnK8jrKWQauAwMc7j
|
|
|
30
30
|
app/config/__pycache__/config.cpython-312.pyc,sha256=dJDpQHukSNdXJe8znD3JmjJjwvfGlDlNCcvG4JwJOrY,769
|
|
31
31
|
app/config/files/liberty.ini,sha256=otQotGlvSYtBGiTEcDhDLNqlmxp2yxYNxWoxPOVOTvo,558
|
|
32
32
|
app/controllers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
33
|
-
app/controllers/api_controller.py,sha256=
|
|
34
|
-
app/controllers/setup_controller.py,sha256=
|
|
33
|
+
app/controllers/api_controller.py,sha256=QEJ2lecgVgU4HpMlQkYMeJkFsDQ1khXmDo8oj57RT2c,2712
|
|
34
|
+
app/controllers/setup_controller.py,sha256=YfrLnlTCK_isIOZbVbHxph95Ma7mTfmsoUBvdWqfzL8,1528
|
|
35
35
|
app/controllers/socket_controller.py,sha256=ok5nbibPEeV4oVx1i_tzoecZwXNocgIUe-ECb_pFAVc,9228
|
|
36
36
|
app/controllers/__pycache__/__init__.cpython-312.pyc,sha256=x4fclq5ptyLRFD1BLp3RPqwh_g1g3NR0v1DI_Y1ofgg,209
|
|
37
|
-
app/controllers/__pycache__/api_controller.cpython-312.pyc,sha256=
|
|
37
|
+
app/controllers/__pycache__/api_controller.cpython-312.pyc,sha256=Q-tJJ8I3Vj6DyrXZ8bTGaYvouw83w9us419veG_3yzU,6606
|
|
38
38
|
app/controllers/__pycache__/react_controller.cpython-312.pyc,sha256=l7_oBeQbJuevc9pd0qZgK7C3QCuOAgR8q2bR_id9xfI,744
|
|
39
|
-
app/controllers/__pycache__/setup_controller.cpython-312.pyc,sha256=
|
|
39
|
+
app/controllers/__pycache__/setup_controller.cpython-312.pyc,sha256=Lw-miWD6XqR996eRAxDaxODqfzJTcg_xz3kfaP59dLw,3666
|
|
40
40
|
app/controllers/__pycache__/socket_controller.cpython-312.pyc,sha256=wKhC-tGf9VOfL2QpfFK-LIwFWHkoSLlfsaYG111SVLk,12191
|
|
41
41
|
app/database/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
42
42
|
app/database/base_dao.py,sha256=eSZx21R3sTsjWUH9AxOg5nyDa0REzDIzR8pwb5Dh-eg,20275
|
|
@@ -54,8 +54,8 @@ app/logs/.DS_Store,sha256=ZGQO-xjQOCQsmmfWoWR3ZEogZ9oYhCs_x2KGpHyB8UQ,6148
|
|
|
54
54
|
app/logs/__init__.py,sha256=xjkrhY9xSUg41sFGl2_tuOWANnaP4fYX8RQAn3NvAFs,514
|
|
55
55
|
app/logs/__pycache__/__init__.cpython-312.pyc,sha256=aGm5f9YknIc8Ip1C4XXk1Of1BvnlqXD16oSHkZSmKRA,889
|
|
56
56
|
app/logs/__pycache__/logs.cpython-312.pyc,sha256=yQEGRGLwe5bK9jD1AaDCdEsEGLO_76eM7V2WQrn2_20,885
|
|
57
|
-
app/logs/files/logs-frontend-json.log,sha256=
|
|
58
|
-
app/logs/files/logs-frontend-text.log,sha256=
|
|
57
|
+
app/logs/files/logs-frontend-json.log,sha256=hrkCwoPzqTvUJNej4oFRPIlCXFY0A4HR3J96FiAwTDE,129501
|
|
58
|
+
app/logs/files/logs-frontend-text.log,sha256=71AD1hm0j_8SMHw4FCL2xVuv_e3YCHRNNUKMydh-yzE,44200
|
|
59
59
|
app/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
60
60
|
app/models/ai.py,sha256=x9QhDHeVly0Paa-GZhkpJBZegS0sZQMmVeJAzAtzmLI,241
|
|
61
61
|
app/models/apidb.py,sha256=OmOVGSc1r1_sK-CzehlPDeo-Pq2H1yT8EUVsiXPizo4,3556
|
|
@@ -64,7 +64,7 @@ app/models/auth.py,sha256=l2gDyUUG3JYJs51VAs8AEg9fmwaQEA10Rq8iqv-vO-Q,1596
|
|
|
64
64
|
app/models/base.py,sha256=BApFmQrW-jdGLvbE4pAP2IgDfDBv0esghcZzhWINoqI,2955
|
|
65
65
|
app/models/modules.py,sha256=nsNeMUO5gTjDJl2YoZXm2Sj85gE2O5Jvh12hIdoP-38,2044
|
|
66
66
|
app/models/pool.py,sha256=WZAzz3gbqBx7XBY_su1OoyROjE1pwSD9q4eRDtE9io4,391
|
|
67
|
-
app/models/setup.py,sha256=
|
|
67
|
+
app/models/setup.py,sha256=BycjwHIbSAl-e_aCqjdmZH3rHFhyFiHcpnZ7BOB02js,887
|
|
68
68
|
app/models/themes.py,sha256=XTCpc2gGF-lg3k8abo_xsneTv3eXTnfHEEYpNLmpdd8,1272
|
|
69
69
|
app/models/__pycache__/__init__.cpython-312.pyc,sha256=THrpQ910DdudL2c8KyLUSgCfpnxMxWh8FV_mHj_qbIw,173
|
|
70
70
|
app/models/__pycache__/ai.cpython-312.pyc,sha256=gVvFXfL65eXGhqSBIkhROTPRPGkhNOWZh1D-9URVUgQ,437
|
|
@@ -75,7 +75,7 @@ app/models/__pycache__/base.cpython-312.pyc,sha256=VoZV64R0hqGT2lt5-lo1iMw0LWzTg
|
|
|
75
75
|
app/models/__pycache__/checkdb.cpython-312.pyc,sha256=xGJ9pEE-GZXiGZu2d-6cNWGt0SKGGevb1LSvWJmqtVY,1741
|
|
76
76
|
app/models/__pycache__/modules.cpython-312.pyc,sha256=Cp9q_eirwSElqIQMxhBuptwfgwXDbQfc9KeyCTywz-A,2244
|
|
77
77
|
app/models/__pycache__/pool.cpython-312.pyc,sha256=mFowin-5N38BbcS-ULmIE-_vCoHV8ND6swQ3rZV-2YA,552
|
|
78
|
-
app/models/__pycache__/setup.cpython-312.pyc,sha256=
|
|
78
|
+
app/models/__pycache__/setup.cpython-312.pyc,sha256=ZO1VHzCcSJdaTJy9RU9jPjVkHrFq-XCrEUvZELX_ZJM,1526
|
|
79
79
|
app/models/__pycache__/themes.cpython-312.pyc,sha256=v6YJHjoKtlbn8gLr4nGnxeuizPi-kJB8cAsoFMkbUTk,1810
|
|
80
80
|
app/postgres/.DS_Store,sha256=1PsY3kXXvWpzmt2RSYnlNMGI6I9r1N0MJWuRml4yjo8,8196
|
|
81
81
|
app/postgres/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -112,22 +112,22 @@ app/public/setup/setup/assets/index-BnpY-7P6.js,sha256=qwUSyqToiQpJw0zkLdZ2dnzXv
|
|
|
112
112
|
app/public/setup/setup/assets/index-BnpY-7P6.js.map,sha256=SxHf_DbBfePZXzpvij_EtZmQcF1NnORVo3hGrW2h1_I,3133938
|
|
113
113
|
app/public/setup/setup/assets/logo_ly-HGj2PB94.svg,sha256=2xnOaSTVYAYU2-ARq5iO80PsKpQveFONztGCoFHl5TU,1466
|
|
114
114
|
app/routes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
115
|
-
app/routes/api_routes.py,sha256=
|
|
115
|
+
app/routes/api_routes.py,sha256=8c4aLp7pqewr3VsZf2tpENnsUk7fGj4ah5uukl6qFis,24125
|
|
116
116
|
app/routes/react_routes.py,sha256=mO52Juyz3SDXHxCKKrRHSN33e-CeJjlEfxqBn-odGo8,1418
|
|
117
|
-
app/routes/setup_routes.py,sha256=
|
|
117
|
+
app/routes/setup_routes.py,sha256=OzMzpEqRXM-srXGmGdsiMelQ-jgqt3RUHj1rfqLHdVc,8391
|
|
118
118
|
app/routes/socket_routes.py,sha256=DjHh03aEFPKWaMPkzjAmrl3jxQ9TyWSdhyTcDzIjdXk,560
|
|
119
119
|
app/routes/__pycache__/__init__.cpython-312.pyc,sha256=Y2jZB16q6GIgs_2KUkT_KQELK64DcIovLJna2lGNqmc,204
|
|
120
|
-
app/routes/__pycache__/api_routes.cpython-312.pyc,sha256=
|
|
120
|
+
app/routes/__pycache__/api_routes.cpython-312.pyc,sha256=hkUKzPrtdzIxoxlO8hGxQ0FkTQMeRKP0nTaY7LvTRL0,23576
|
|
121
121
|
app/routes/__pycache__/react_routes.cpython-312.pyc,sha256=5iiOkojr1yV68ORi_dtOhJIAlrMkICjN5AxOHCWTGpQ,2302
|
|
122
|
-
app/routes/__pycache__/setup_routes.cpython-312.pyc,sha256=
|
|
122
|
+
app/routes/__pycache__/setup_routes.cpython-312.pyc,sha256=zF4YBdXOKZFPIwEnN703SQkroFbJSn69HYLDc4GO7kM,10105
|
|
123
123
|
app/routes/__pycache__/socket_routes.cpython-312.pyc,sha256=T46m7FBCH1DxdBIVjyQbd_geYdQRo9O_ZpXNvlghHT8,1419
|
|
124
124
|
app/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
125
|
-
app/services/api_rest.py,sha256=
|
|
125
|
+
app/services/api_rest.py,sha256=w-dIFj-cVGYLNxWTNwjkTibdBX0isK8jeihAwPtv06k,12295
|
|
126
126
|
app/services/db_pool.py,sha256=gLWCEQ7Pq_6gKoChHJkQH7YmnZoNEMrN6P1MJUiAUec,2043
|
|
127
127
|
app/services/db_query.py,sha256=IoLrWI4JmZjGP4GgswmXih0IRpvRs7SNNfqREjbrWtE,27475
|
|
128
128
|
app/services/__pycache__/__init__.cpython-312.pyc,sha256=mcbX3fcnuuxC4RYKQ4PrUp2hLOg-29jJQ8XvXvLoxhw,206
|
|
129
129
|
app/services/__pycache__/alembic.cpython-312.pyc,sha256=rIS4OWJ4gPJjrUkyalK7wcQ3mWGXTnKZ_OprVObmJ9k,3036
|
|
130
|
-
app/services/__pycache__/api_rest.cpython-312.pyc,sha256=
|
|
130
|
+
app/services/__pycache__/api_rest.cpython-312.pyc,sha256=YnFtnoPzwcR6IFa9lqJtggZjUXrDQxSGwKnpcHqDIFk,16206
|
|
131
131
|
app/services/__pycache__/db_pool.cpython-312.pyc,sha256=C9NB4znvWLa_kTgbwDK2xBVQ7VR4DLPbVHtHrNTISmA,4076
|
|
132
132
|
app/services/__pycache__/db_query.cpython-312.pyc,sha256=1A1An2UJ7DyUbPEatKQEFPKDHW6XiM5TLuBELjXGKDI,32790
|
|
133
133
|
app/setup/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -154,16 +154,16 @@ app/setup/models/__pycache__/nomasx1.cpython-312.pyc,sha256=4FwPwbwppSeahC7_rGPR
|
|
|
154
154
|
app/setup/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
155
155
|
app/setup/services/alembic.py,sha256=EoV5Cf_TjutwFKLFquFTAzG-Pfhbd8imlcTL4YkvSsA,3144
|
|
156
156
|
app/setup/services/dump.py,sha256=7pJwwB351kosN-4WHkSagKYL5fzGwCYc6YROovRzQI4,7552
|
|
157
|
-
app/setup/services/install.py,sha256=
|
|
157
|
+
app/setup/services/install.py,sha256=txzO6KRjzc1J7tX2Z8IR6I3DscFtr9TRcxKJDX5uFYM,8779
|
|
158
158
|
app/setup/services/models.py,sha256=M3FHSDXvFYBdqYNF_3bzYY3UfKXjgy_jQFqkqn-SP-k,10039
|
|
159
|
-
app/setup/services/setup.py,sha256=
|
|
159
|
+
app/setup/services/setup.py,sha256=dW0G9tRYOdvY7WIy0bxb8qfRX0nXk2BtIBcH9vNxv5c,23852
|
|
160
160
|
app/setup/services/__pycache__/__init__.cpython-312.pyc,sha256=yjoKcph4MN-OJKFAhvDN9iY2UkBwAeM-aUmJj55Mf2g,180
|
|
161
161
|
app/setup/services/__pycache__/alembic.cpython-312.pyc,sha256=PIkNJJIC8K_3wPGHBQzL9l1mR5cJ25h4XWNb4lUXnPQ,4638
|
|
162
162
|
app/setup/services/__pycache__/dump.cpython-312.pyc,sha256=TfXpk2EWcrBeOY-IafRWiTopYdmM2FAMlRWPj9tZIOw,11289
|
|
163
163
|
app/setup/services/__pycache__/init.cpython-312.pyc,sha256=EiUBfSie7VtB2HOL8Mq1xwCXP29NpynX-eR14fE16xQ,4561
|
|
164
|
-
app/setup/services/__pycache__/install.cpython-312.pyc,sha256=
|
|
164
|
+
app/setup/services/__pycache__/install.cpython-312.pyc,sha256=3XBrnQIgd7Fv5L1zBTwo1zoXZC7MIrZhOZEzT6gUs-w,12026
|
|
165
165
|
app/setup/services/__pycache__/models.cpython-312.pyc,sha256=ae3uMFVs5G1uOTwqI5dhh-YCwviUlGkSILBQ248gbVc,11084
|
|
166
|
-
app/setup/services/__pycache__/setup.cpython-312.pyc,sha256=
|
|
166
|
+
app/setup/services/__pycache__/setup.cpython-312.pyc,sha256=0m8Ipuj9aaU4P_9Im4sUdep-oPWLeBMEWjavi_Js71M,26155
|
|
167
167
|
app/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
168
168
|
app/utils/common.py,sha256=UrbbEeqwJ9WXxgTixlOI6wQl06OJBNtEVabiTN77EVc,355
|
|
169
169
|
app/utils/encrypt.py,sha256=4rPgyIlgZv3LiNl9uWAQafpqvcRV9XB5PW_c0hKXYHo,2819
|
|
@@ -203,9 +203,9 @@ tests/scenarios/__pycache__/test_fmw_applications.cpython-312-pytest-8.3.4.pyc,s
|
|
|
203
203
|
tests/scenarios/__pycache__/test_fmw_encrypt.cpython-312-pytest-8.3.4.pyc,sha256=VllhpnVjcJtvmJhaagWcDQpzD1GxZ72eFT4yoOy3Bck,2880
|
|
204
204
|
tests/scenarios/__pycache__/test_fmw_modules.cpython-312-pytest-8.3.4.pyc,sha256=ccuuDE4SD_TRq98nKRwoIBPztSAnVw5RaSoXjeaWQv4,3837
|
|
205
205
|
tests/scenarios/__pycache__/test_fmw_themes.cpython-312-pytest-8.3.4.pyc,sha256=rfQXJFo9xKCdkxbytBnmLpAKgvk9vW475Ov-3ENBm2o,4900
|
|
206
|
-
liberty_framework-6.0.
|
|
207
|
-
liberty_framework-6.0.
|
|
208
|
-
liberty_framework-6.0.
|
|
209
|
-
liberty_framework-6.0.
|
|
210
|
-
liberty_framework-6.0.
|
|
211
|
-
liberty_framework-6.0.
|
|
206
|
+
liberty_framework-6.0.46.dist-info/LICENSE,sha256=ILBn-G3jdarm2w8oOrLmXeJNU3czuJvVhDLBASWdhM8,34522
|
|
207
|
+
liberty_framework-6.0.46.dist-info/METADATA,sha256=G67TasPY20_huedx7GO_jNCGTTVPZNbPBY61Q_KKpj8,7941
|
|
208
|
+
liberty_framework-6.0.46.dist-info/WHEEL,sha256=nn6H5-ilmfVryoAQl3ZQ2l8SH5imPWFpm1A5FgEuFV4,91
|
|
209
|
+
liberty_framework-6.0.46.dist-info/entry_points.txt,sha256=iVkIVLRG-sd7KeOUBSf7Ew_1ckUK5hvZZocaTh3Nq6Q,48
|
|
210
|
+
liberty_framework-6.0.46.dist-info/top_level.txt,sha256=MJXn5pCZl0XSEeWNKzshxzMMANr3AymeoMYBO1JNPyU,10
|
|
211
|
+
liberty_framework-6.0.46.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|