diff --git a/src/ocab_core/ocab_core/main.py b/src/ocab_core/ocab_core/main.py
index 71f630c..83a0b5c 100644
--- a/src/ocab_core/ocab_core/main.py
+++ b/src/ocab_core/ocab_core/main.py
@@ -78,7 +78,7 @@ class OCAB:
singleton = Singleton()
app = FastAPI()
config = get_module("standard.config", "config")
- app.mount("/webapp", singleton.storage["webapp"])
+ app.mount(config.get("miniapp::prefix"), singleton.storage["webapp"])
await register_bot_webhook(app, singleton.bot, singleton.dp)
await singleton.bot.set_webhook(config.get("core::webhook::public_url"))
hyperConfig = HyperConfig()
diff --git a/src/ocab_modules/ocab_modules/standard/config/main.py b/src/ocab_modules/ocab_modules/standard/config/main.py
index b482db8..89096a0 100644
--- a/src/ocab_modules/ocab_modules/standard/config/main.py
+++ b/src/ocab_modules/ocab_modules/standard/config/main.py
@@ -15,6 +15,7 @@ def register_settings_page():
path="/settings",
blueprint=get_miniapp_blueprint(config, prefix),
prefix=prefix,
+ role="ADMIN",
)
pass
diff --git a/src/ocab_modules/ocab_modules/standard/config/miniapp_ui.py b/src/ocab_modules/ocab_modules/standard/config/miniapp_ui.py
index 53c44d6..cd6fabd 100644
--- a/src/ocab_modules/ocab_modules/standard/config/miniapp_ui.py
+++ b/src/ocab_modules/ocab_modules/standard/config/miniapp_ui.py
@@ -1,13 +1,22 @@
+import asyncio
+from typing import TYPE_CHECKING
+
from .config_manager import ConfigManager
try:
import dash_bootstrap_components as dbc
+ import flask
from dash_extensions.enrich import ALL, Input, Output, State, dcc, html
DASH_AVAILABLE = True
except ImportError:
DASH_AVAILABLE = False
+from ocab_core.modules_system.public_api import get_module
+
+if TYPE_CHECKING:
+ from ocab_modules.standard.roles import Roles as IRoles
+
def create_control(key: str, config: ConfigManager):
value = config.get(key)
@@ -126,6 +135,10 @@ def create_settings_components(tree, level=0):
def get_miniapp_blueprint(config: ConfigManager, prefix: str):
+ Roles: "type[IRoles]" = get_module("standard.roles", "Roles")
+
+ roles = Roles()
+
import datetime
from dash_extensions.enrich import DashBlueprint
@@ -171,6 +184,28 @@ def get_miniapp_blueprint(config: ConfigManager, prefix: str):
)
def save_settings(n_clicks, values, keys):
if n_clicks > 0:
+ user = getattr(flask.g, "user", None)
+
+ if user is None:
+ return (
+ dbc.Alert(
+ "Вы не авторизованы!",
+ color="danger",
+ duration=10000,
+ ),
+ "-",
+ )
+
+ if not asyncio.run(roles.check_admin_permission(user["id"])):
+ return (
+ dbc.Alert(
+ "Вы не администратор!",
+ color="danger",
+ duration=10000,
+ ),
+ "-",
+ )
+
# TODO: добавить валидацию значений
updated_settings = {}
@@ -178,12 +213,18 @@ def get_miniapp_blueprint(config: ConfigManager, prefix: str):
key: str = id_dict["key"]
if prefix:
key = key.removeprefix(f"{prefix}-")
- updated_settings[key] = value
+
+ meta = config.get_meta(key)
+
+ if meta["type"] == "password":
+ if value: # Only update if a new value is provided
+ updated_settings[key] = value
+ else:
+ updated_settings[key] = value
config.mass_set(updated_settings)
config.save()
- # locale.setlocale(locale.LC_TIME, "ru_RU.UTF-8")
now = datetime.datetime.now()
date_str = now.strftime("%H:%M:%S")
diff --git a/src/ocab_modules/ocab_modules/standard/miniapp/dash_telegram_auth.py b/src/ocab_modules/ocab_modules/standard/miniapp/dash_telegram_auth.py
index 6bf5c9d..5137770 100644
--- a/src/ocab_modules/ocab_modules/standard/miniapp/dash_telegram_auth.py
+++ b/src/ocab_modules/ocab_modules/standard/miniapp/dash_telegram_auth.py
@@ -1,13 +1,104 @@
import flask
from aiogram.utils.web_app import safe_parse_webapp_init_data
from dash import Dash
-from dash_extensions.enrich import Input
+from dash_extensions.enrich import Input, Output
from flask import request
+# TODO: добавить прокидывание BASE_PATH, т.к. это параметр из настроек
+
+WEBAPP_LOADER_TEMPLATE = """
+
+
+
+
+
+ OCAB
+
+
+
+
+
+ Loading...
+
+
+
+
+"""
+
def get_auth_server(bot_token: str):
server = flask.Flask(__name__)
+ @server.route("/")
+ @server.route("/")
+ def webapp_loader(rest=None):
+ return flask.Response(WEBAPP_LOADER_TEMPLATE, mimetype="text/html")
+
@server.before_request
def add_auth_data():
init_data = request.cookies.get("tg_init_data")
@@ -21,13 +112,35 @@ def get_auth_server(bot_token: str):
return server
-def setup_auth_clientcallback(app: Dash):
+def setup_auth_clientcallbacks(app: Dash):
app.clientside_callback(
"""
- function(n_inervals) {
- const tg = window.Telegram.WebApp;
- document.cookie = `tg_init_data=${JSON.stringify(tg.initData)}; path=/`;
+ function(n_intervals) {
+ if (window.webAppData) {
+ return window.webAppData;
+ }
+
+ function receiveMessage(event) {
+ if (event.data.type === 'webAppData') {
+ window.webAppData = event.data.webApp;
+ window.removeEventListener('message', receiveMessage);
+ }
+ }
+
+ window.addEventListener('message', receiveMessage, false);
+
+ return window.dash_clientside.no_update;
}
""",
- Input("init-telegram-interval", "n_intervals"),
+ Output("hidden-div", "children"),
+ Input("interval-component", "n_intervals"),
+ )
+
+ app.clientside_callback(
+ """
+ function(pathname) {
+ window.parent.postMessage({ type: 'iframe-url-changed', pathname }, '*');
+ }
+ """,
+ Input("url", "pathname"),
)
diff --git a/src/ocab_modules/ocab_modules/standard/miniapp/lib.py b/src/ocab_modules/ocab_modules/standard/miniapp/lib.py
index 64273cd..9768ae9 100644
--- a/src/ocab_modules/ocab_modules/standard/miniapp/lib.py
+++ b/src/ocab_modules/ocab_modules/standard/miniapp/lib.py
@@ -1,26 +1,30 @@
+import asyncio
from collections import OrderedDict
from typing import TYPE_CHECKING
import dash
import dash_bootstrap_components as dbc
+import flask
from dash_extensions.enrich import DashBlueprint, DashProxy, Input, Output, dcc, html
from dash_extensions.pages import setup_page_components
-from ocab_core.modules_system.public_api import get_module
+from ocab_core.modules_system.public_api import get_module, log
-from .dash_telegram_auth import get_auth_server, setup_auth_clientcallback
+from .dash_telegram_auth import get_auth_server, setup_auth_clientcallbacks
if TYPE_CHECKING:
from ocab_modules.standard.config import IConfig
+ from ocab_modules.standard.roles import Roles as IRoles
pages = OrderedDict()
-def register_page(name, path, blueprint, prefix=""):
+def register_page(name, path, blueprint, prefix="", role="USER"):
pages[path] = {
"name": name,
"blueprint": blueprint,
"prefix": prefix,
+ "role": role,
}
@@ -33,9 +37,14 @@ def register_home_page():
register_home_page()
config: "IConfig" = get_module("standard.config", "config")
+Roles: "type[IRoles]" = get_module("standard.roles", "Roles")
def create_dash_app(requests_pathname_prefix: str = None) -> dash.Dash:
+ log(requests_pathname_prefix)
+
+ real_prefix = f"{requests_pathname_prefix}_internal/"
+
server = get_auth_server(config.get("core::token"))
app = DashProxy(
@@ -47,10 +56,13 @@ def create_dash_app(requests_pathname_prefix: str = None) -> dash.Dash:
dbc.icons.BOOTSTRAP,
],
external_scripts=[
+ #
"https://telegram.org/js/telegram-web-app.js"
- ], # Add Telegram Mini Apps script to
+ ],
server=server,
- requests_pathname_prefix=requests_pathname_prefix,
+ requests_pathname_prefix=real_prefix,
+ routes_pathname_prefix="/_internal/",
+ # requests_pathname_prefix=requests_pathname_prefix,
meta_tags=[
{"name": "viewport", "content": "width=device-width, initial-scale=1"},
],
@@ -63,30 +75,8 @@ def create_dash_app(requests_pathname_prefix: str = None) -> dash.Dash:
# Register pages
for path, page in pages.items():
- # dash.register_page(page["name"], path=path, layout=page["layout"])
page["blueprint"].register(app, path, prefix=page["prefix"])
- # Create sidebar
- sidebar = dbc.Offcanvas(
- id="offcanvas",
- title="Меню",
- is_open=False,
- children=[
- dbc.Nav(
- [
- dbc.NavLink(
- page["name"],
- href=f"{requests_pathname_prefix}{path.lstrip('/')}",
- id={"type": "nav-link", "index": path},
- )
- for path, page in pages.items()
- ],
- vertical=True,
- pills=True,
- ),
- ],
- )
-
# Create navbar
navbar = dbc.Navbar(
dbc.Container(
@@ -104,24 +94,67 @@ def create_dash_app(requests_pathname_prefix: str = None) -> dash.Dash:
dark=True,
)
- # Define app layout
- app.layout = html.Div(
- [
- dcc.Location(id="url", refresh=False),
- dcc.Interval(
- id="init-telegram-interval",
- interval=100,
- n_intervals=0,
- max_intervals=1,
- ),
- navbar,
- sidebar,
- dash.page_container,
- setup_page_components(),
- ]
- )
+ roles = Roles()
- setup_auth_clientcallback(app)
+ def create_layout():
+ user = getattr(flask.g, "user", None)
+
+ if not user:
+ return html.Div()
+
+ user_id = user["id"]
+ user_permission = asyncio.run(roles.get_user_permission(user_id)) or "USER"
+
+ available_pages = {
+ path: page
+ for path, page in pages.items()
+ if (isinstance(page["role"], list) and user_permission in page["role"])
+ or page["role"] == user_permission
+ or page["role"] == "USER"
+ }
+
+ # Create sidebar
+ sidebar = dbc.Offcanvas(
+ id="offcanvas",
+ title="Меню",
+ is_open=False,
+ children=[
+ dbc.Nav(
+ [
+ dbc.NavLink(
+ page["name"],
+ href=f"{real_prefix}/{path.lstrip('/')}",
+ id={"type": "nav-link", "index": path},
+ )
+ for path, page in available_pages.items()
+ ],
+ vertical=True,
+ pills=True,
+ ),
+ ],
+ )
+
+ layout = html.Div(
+ [
+ dcc.Location(id="url", refresh=False),
+ dcc.Interval(
+ id="init-telegram-interval",
+ interval=100,
+ n_intervals=0,
+ max_intervals=1,
+ ),
+ navbar,
+ sidebar,
+ dash.page_container,
+ setup_page_components(),
+ ]
+ )
+
+ return layout
+
+ app.layout = create_layout
+
+ setup_auth_clientcallbacks(app)
# Открытие на кнопку меню
app.clientside_callback(