mirror of
				https://gitflic.ru/project/maks1ms/ocab.git
				synced 2025-11-04 15:11:25 +03:00 
			
		
		
		
	wip
This commit is contained in:
		@@ -78,7 +78,7 @@ class OCAB:
 | 
				
			|||||||
        singleton = Singleton()
 | 
					        singleton = Singleton()
 | 
				
			||||||
        app = FastAPI()
 | 
					        app = FastAPI()
 | 
				
			||||||
        config = get_module("standard.config", "config")
 | 
					        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 register_bot_webhook(app, singleton.bot, singleton.dp)
 | 
				
			||||||
        await singleton.bot.set_webhook(config.get("core::webhook::public_url"))
 | 
					        await singleton.bot.set_webhook(config.get("core::webhook::public_url"))
 | 
				
			||||||
        hyperConfig = HyperConfig()
 | 
					        hyperConfig = HyperConfig()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,6 +15,7 @@ def register_settings_page():
 | 
				
			|||||||
            path="/settings",
 | 
					            path="/settings",
 | 
				
			||||||
            blueprint=get_miniapp_blueprint(config, prefix),
 | 
					            blueprint=get_miniapp_blueprint(config, prefix),
 | 
				
			||||||
            prefix=prefix,
 | 
					            prefix=prefix,
 | 
				
			||||||
 | 
					            role="ADMIN",
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        pass
 | 
					        pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,13 +1,22 @@
 | 
				
			|||||||
 | 
					import asyncio
 | 
				
			||||||
 | 
					from typing import TYPE_CHECKING
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from .config_manager import ConfigManager
 | 
					from .config_manager import ConfigManager
 | 
				
			||||||
 | 
					
 | 
				
			||||||
try:
 | 
					try:
 | 
				
			||||||
    import dash_bootstrap_components as dbc
 | 
					    import dash_bootstrap_components as dbc
 | 
				
			||||||
 | 
					    import flask
 | 
				
			||||||
    from dash_extensions.enrich import ALL, Input, Output, State, dcc, html
 | 
					    from dash_extensions.enrich import ALL, Input, Output, State, dcc, html
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    DASH_AVAILABLE = True
 | 
					    DASH_AVAILABLE = True
 | 
				
			||||||
except ImportError:
 | 
					except ImportError:
 | 
				
			||||||
    DASH_AVAILABLE = False
 | 
					    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):
 | 
					def create_control(key: str, config: ConfigManager):
 | 
				
			||||||
    value = config.get(key)
 | 
					    value = config.get(key)
 | 
				
			||||||
@@ -126,6 +135,10 @@ def create_settings_components(tree, level=0):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def get_miniapp_blueprint(config: ConfigManager, prefix: str):
 | 
					def get_miniapp_blueprint(config: ConfigManager, prefix: str):
 | 
				
			||||||
 | 
					    Roles: "type[IRoles]" = get_module("standard.roles", "Roles")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    roles = Roles()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    import datetime
 | 
					    import datetime
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    from dash_extensions.enrich import DashBlueprint
 | 
					    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):
 | 
					    def save_settings(n_clicks, values, keys):
 | 
				
			||||||
        if n_clicks > 0:
 | 
					        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: добавить валидацию значений
 | 
					            # TODO: добавить валидацию значений
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            updated_settings = {}
 | 
					            updated_settings = {}
 | 
				
			||||||
@@ -178,12 +213,18 @@ def get_miniapp_blueprint(config: ConfigManager, prefix: str):
 | 
				
			|||||||
                key: str = id_dict["key"]
 | 
					                key: str = id_dict["key"]
 | 
				
			||||||
                if prefix:
 | 
					                if prefix:
 | 
				
			||||||
                    key = key.removeprefix(f"{prefix}-")
 | 
					                    key = key.removeprefix(f"{prefix}-")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                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
 | 
					                    updated_settings[key] = value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            config.mass_set(updated_settings)
 | 
					            config.mass_set(updated_settings)
 | 
				
			||||||
            config.save()
 | 
					            config.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # locale.setlocale(locale.LC_TIME, "ru_RU.UTF-8")
 | 
					 | 
				
			||||||
            now = datetime.datetime.now()
 | 
					            now = datetime.datetime.now()
 | 
				
			||||||
            date_str = now.strftime("%H:%M:%S")
 | 
					            date_str = now.strftime("%H:%M:%S")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,13 +1,104 @@
 | 
				
			|||||||
import flask
 | 
					import flask
 | 
				
			||||||
from aiogram.utils.web_app import safe_parse_webapp_init_data
 | 
					from aiogram.utils.web_app import safe_parse_webapp_init_data
 | 
				
			||||||
from dash import Dash
 | 
					from dash import Dash
 | 
				
			||||||
from dash_extensions.enrich import Input
 | 
					from dash_extensions.enrich import Input, Output
 | 
				
			||||||
from flask import request
 | 
					from flask import request
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# TODO: добавить прокидывание BASE_PATH, т.к. это параметр из настроек
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					WEBAPP_LOADER_TEMPLATE = """
 | 
				
			||||||
 | 
					<!DOCTYPE html>
 | 
				
			||||||
 | 
					<html lang="ru">
 | 
				
			||||||
 | 
					<head>
 | 
				
			||||||
 | 
					    <meta charset="UTF-8">
 | 
				
			||||||
 | 
					    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | 
				
			||||||
 | 
					    <title>OCAB</title>
 | 
				
			||||||
 | 
					    <script src="https://telegram.org/js/telegram-web-app.js"></script>
 | 
				
			||||||
 | 
					    <script>
 | 
				
			||||||
 | 
					        window.addEventListener('message', function(event) {
 | 
				
			||||||
 | 
					            if (event.origin !== window.location.origin) return;
 | 
				
			||||||
 | 
					            if (event.data.type === 'iframe-url-changed') {
 | 
				
			||||||
 | 
					                history.pushState(
 | 
				
			||||||
 | 
					                    null,
 | 
				
			||||||
 | 
					                    '',
 | 
				
			||||||
 | 
					                    window.BASE_PATH + event.data.pathname.substring(
 | 
				
			||||||
 | 
					                        window.INTERNAL_PATH.length
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        window.addEventListener('popstate', function(event) {
 | 
				
			||||||
 | 
					            var iframe = document.getElementById('app-frame');
 | 
				
			||||||
 | 
					            var iframeWindow = iframe.contentWindow;
 | 
				
			||||||
 | 
					            iframeWindow.history.back();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    </script>
 | 
				
			||||||
 | 
					    <style>
 | 
				
			||||||
 | 
					        #app-frame {
 | 
				
			||||||
 | 
					            display:none;
 | 
				
			||||||
 | 
					            width:100%;
 | 
				
			||||||
 | 
					            height:100vh;
 | 
				
			||||||
 | 
					            border:none;
 | 
				
			||||||
 | 
					            position: absolute;
 | 
				
			||||||
 | 
					            top: 0;
 | 
				
			||||||
 | 
					            left: 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    </style>
 | 
				
			||||||
 | 
					</head>
 | 
				
			||||||
 | 
					<body>
 | 
				
			||||||
 | 
					    <div id="loading">Loading...</div>
 | 
				
			||||||
 | 
					    <iframe id="app-frame"></iframe>
 | 
				
			||||||
 | 
					    <script>
 | 
				
			||||||
 | 
					    document.addEventListener('DOMContentLoaded', function() {
 | 
				
			||||||
 | 
					        const tg = window.Telegram.WebApp;
 | 
				
			||||||
 | 
					        document.cookie = `tg_init_data=${JSON.stringify(tg.initData)}; path=/`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // if (!tg.initData) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const iframe = document.getElementById('app-frame');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Константы для путей
 | 
				
			||||||
 | 
					        const BASE_PATH = '/webapp';
 | 
				
			||||||
 | 
					        const INTERNAL_PATH = '/webapp/_internal';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        window.BASE_PATH = BASE_PATH;
 | 
				
			||||||
 | 
					        window.INTERNAL_PATH = INTERNAL_PATH
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Текущий путь страницы
 | 
				
			||||||
 | 
					        const currentPath = window.location.pathname;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Формируем новый путь для iframe
 | 
				
			||||||
 | 
					        let iframeSrc = INTERNAL_PATH;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Если текущий путь начинается с BASE_PATH, убираем BASE_PATH из текущего пути
 | 
				
			||||||
 | 
					        if (currentPath.startsWith(BASE_PATH)
 | 
				
			||||||
 | 
					        && currentPath.length > BASE_PATH.length) {
 | 
				
			||||||
 | 
					            iframeSrc += currentPath.substring(BASE_PATH.length);
 | 
				
			||||||
 | 
					        } else if (currentPath !== '/') {
 | 
				
			||||||
 | 
					            iframeSrc += currentPath;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        iframe.src = iframeSrc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        iframe.onload = function() {
 | 
				
			||||||
 | 
					            document.getElementById('loading').style.display = 'none';
 | 
				
			||||||
 | 
					            iframe.style.display = 'block';
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    </script>
 | 
				
			||||||
 | 
					</body>
 | 
				
			||||||
 | 
					</html>
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def get_auth_server(bot_token: str):
 | 
					def get_auth_server(bot_token: str):
 | 
				
			||||||
    server = flask.Flask(__name__)
 | 
					    server = flask.Flask(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @server.route("/<path:rest>")
 | 
				
			||||||
 | 
					    @server.route("/")
 | 
				
			||||||
 | 
					    def webapp_loader(rest=None):
 | 
				
			||||||
 | 
					        return flask.Response(WEBAPP_LOADER_TEMPLATE, mimetype="text/html")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @server.before_request
 | 
					    @server.before_request
 | 
				
			||||||
    def add_auth_data():
 | 
					    def add_auth_data():
 | 
				
			||||||
        init_data = request.cookies.get("tg_init_data")
 | 
					        init_data = request.cookies.get("tg_init_data")
 | 
				
			||||||
@@ -21,13 +112,35 @@ def get_auth_server(bot_token: str):
 | 
				
			|||||||
    return server
 | 
					    return server
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def setup_auth_clientcallback(app: Dash):
 | 
					def setup_auth_clientcallbacks(app: Dash):
 | 
				
			||||||
    app.clientside_callback(
 | 
					    app.clientside_callback(
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        function(n_inervals) {
 | 
					        function(n_intervals) {
 | 
				
			||||||
            const tg = window.Telegram.WebApp;
 | 
					            if (window.webAppData) {
 | 
				
			||||||
            document.cookie = `tg_init_data=${JSON.stringify(tg.initData)}; path=/`;
 | 
					                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"),
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,26 +1,30 @@
 | 
				
			|||||||
 | 
					import asyncio
 | 
				
			||||||
from collections import OrderedDict
 | 
					from collections import OrderedDict
 | 
				
			||||||
from typing import TYPE_CHECKING
 | 
					from typing import TYPE_CHECKING
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import dash
 | 
					import dash
 | 
				
			||||||
import dash_bootstrap_components as dbc
 | 
					import dash_bootstrap_components as dbc
 | 
				
			||||||
 | 
					import flask
 | 
				
			||||||
from dash_extensions.enrich import DashBlueprint, DashProxy, Input, Output, dcc, html
 | 
					from dash_extensions.enrich import DashBlueprint, DashProxy, Input, Output, dcc, html
 | 
				
			||||||
from dash_extensions.pages import setup_page_components
 | 
					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:
 | 
					if TYPE_CHECKING:
 | 
				
			||||||
    from ocab_modules.standard.config import IConfig
 | 
					    from ocab_modules.standard.config import IConfig
 | 
				
			||||||
 | 
					    from ocab_modules.standard.roles import Roles as IRoles
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pages = OrderedDict()
 | 
					pages = OrderedDict()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def register_page(name, path, blueprint, prefix=""):
 | 
					def register_page(name, path, blueprint, prefix="", role="USER"):
 | 
				
			||||||
    pages[path] = {
 | 
					    pages[path] = {
 | 
				
			||||||
        "name": name,
 | 
					        "name": name,
 | 
				
			||||||
        "blueprint": blueprint,
 | 
					        "blueprint": blueprint,
 | 
				
			||||||
        "prefix": prefix,
 | 
					        "prefix": prefix,
 | 
				
			||||||
 | 
					        "role": role,
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -33,9 +37,14 @@ def register_home_page():
 | 
				
			|||||||
register_home_page()
 | 
					register_home_page()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
config: "IConfig" = get_module("standard.config", "config")
 | 
					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:
 | 
					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"))
 | 
					    server = get_auth_server(config.get("core::token"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    app = DashProxy(
 | 
					    app = DashProxy(
 | 
				
			||||||
@@ -47,10 +56,13 @@ def create_dash_app(requests_pathname_prefix: str = None) -> dash.Dash:
 | 
				
			|||||||
            dbc.icons.BOOTSTRAP,
 | 
					            dbc.icons.BOOTSTRAP,
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
        external_scripts=[
 | 
					        external_scripts=[
 | 
				
			||||||
 | 
					            #
 | 
				
			||||||
            "https://telegram.org/js/telegram-web-app.js"
 | 
					            "https://telegram.org/js/telegram-web-app.js"
 | 
				
			||||||
        ],  # Add Telegram Mini Apps script to <head>
 | 
					        ],
 | 
				
			||||||
        server=server,
 | 
					        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=[
 | 
					        meta_tags=[
 | 
				
			||||||
            {"name": "viewport", "content": "width=device-width, initial-scale=1"},
 | 
					            {"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
 | 
					    # Register pages
 | 
				
			||||||
    for path, page in pages.items():
 | 
					    for path, page in pages.items():
 | 
				
			||||||
        # dash.register_page(page["name"], path=path, layout=page["layout"])
 | 
					 | 
				
			||||||
        page["blueprint"].register(app, path, prefix=page["prefix"])
 | 
					        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
 | 
					    # Create navbar
 | 
				
			||||||
    navbar = dbc.Navbar(
 | 
					    navbar = dbc.Navbar(
 | 
				
			||||||
        dbc.Container(
 | 
					        dbc.Container(
 | 
				
			||||||
@@ -104,8 +94,47 @@ def create_dash_app(requests_pathname_prefix: str = None) -> dash.Dash:
 | 
				
			|||||||
        dark=True,
 | 
					        dark=True,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Define app layout
 | 
					    roles = Roles()
 | 
				
			||||||
    app.layout = html.Div(
 | 
					
 | 
				
			||||||
 | 
					    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.Location(id="url", refresh=False),
 | 
				
			||||||
                dcc.Interval(
 | 
					                dcc.Interval(
 | 
				
			||||||
@@ -121,7 +150,11 @@ def create_dash_app(requests_pathname_prefix: str = None) -> dash.Dash:
 | 
				
			|||||||
            ]
 | 
					            ]
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    setup_auth_clientcallback(app)
 | 
					        return layout
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    app.layout = create_layout
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    setup_auth_clientcallbacks(app)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Открытие на кнопку меню
 | 
					    # Открытие на кнопку меню
 | 
				
			||||||
    app.clientside_callback(
 | 
					    app.clientside_callback(
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user