242 lines
11 KiB
Python
242 lines
11 KiB
Python
from datetime import datetime
|
|
from pathlib import Path
|
|
from string import Template
|
|
|
|
from .config import TMPL_DIR, CSS_PATH, PAGES_CSS_DIR, JS_DIR, _state, DbType, _auth, _conn, ConnType
|
|
from .queries.db import e as _e
|
|
|
|
|
|
def _css() -> str:
|
|
return CSS_PATH.read_text(encoding='utf-8')
|
|
|
|
|
|
def _page_subdirs(base: Path) -> list:
|
|
"""Return direct subdirectories of base, excluding 'shared'."""
|
|
return [d for d in sorted(base.iterdir()) if d.is_dir() and d.name != 'shared']
|
|
|
|
|
|
def _page_css(name: str) -> str:
|
|
parts = []
|
|
shared = PAGES_CSS_DIR / 'filters.css'
|
|
if shared.exists():
|
|
parts.append(shared.read_text(encoding='utf-8'))
|
|
for search_dir in [PAGES_CSS_DIR] + _page_subdirs(PAGES_CSS_DIR):
|
|
main = search_dir / f'{name}.css'
|
|
if main.exists():
|
|
parts.append(main.read_text(encoding='utf-8'))
|
|
for f in sorted(search_dir.glob(f'{name}_*.css')):
|
|
parts.append(f.read_text(encoding='utf-8'))
|
|
return '\n'.join(parts)
|
|
|
|
|
|
def _page_js(name: str) -> str:
|
|
parts = []
|
|
shared_dir = JS_DIR / 'shared'
|
|
if shared_dir.exists():
|
|
for f in sorted(shared_dir.glob('*.js')):
|
|
parts.append(f.read_text(encoding='utf-8'))
|
|
for search_dir in [JS_DIR] + _page_subdirs(JS_DIR):
|
|
main = search_dir / f'page_{name}.js'
|
|
if main.exists():
|
|
parts.append(main.read_text(encoding='utf-8'))
|
|
for f in sorted(search_dir.glob(f'page_{name}_*.js')):
|
|
parts.append(f.read_text(encoding='utf-8'))
|
|
return '\n'.join(parts)
|
|
|
|
|
|
def _tmpl(name: str) -> Template:
|
|
return Template((TMPL_DIR / name).read_text(encoding='utf-8'))
|
|
|
|
|
|
def _card(label, value, color='#2563eb') -> str:
|
|
return (
|
|
f'<div class="card">'
|
|
f'<div class="cv" style="color:{color}">{_e(value)}</div>'
|
|
f'<div class="cl">{_e(label)}</div>'
|
|
f'</div>'
|
|
)
|
|
|
|
|
|
def _build_db_conn_modal() -> str:
|
|
is_pg = _conn['type'] == ConnType.POSTGRES
|
|
inp = (
|
|
'style="width:100%;padding:.5rem .75rem;border:1.5px solid #cbd5e1;'
|
|
'border-radius:6px;font-size:.9rem;box-sizing:border-box"'
|
|
)
|
|
lbl = 'style="font-size:.8rem;font-weight:600;color:#475569;display:block;margin-bottom:.25rem"'
|
|
badge_sqlite = (
|
|
'<span class="badge ok" style="font-size:.85rem;padding:4px 12px">📄 SQLite</span>'
|
|
)
|
|
badge_pg = (
|
|
'<span class="badge" style="font-size:.85rem;padding:4px 12px;background:#7c3aed;color:#fff">'
|
|
'📡 PostgreSQL</span>'
|
|
)
|
|
current_badge = badge_pg if is_pg else badge_sqlite
|
|
|
|
pg_host = _e(_conn.get('pg_host', 'localhost'))
|
|
pg_port = _e(_conn.get('pg_port', '5432'))
|
|
pg_db = _e(_conn.get('pg_db', ''))
|
|
pg_user = _e(_conn.get('pg_user', ''))
|
|
|
|
body = (
|
|
'<p class="modal-subtitle">Sorgente dati attuale:</p>'
|
|
f'<p style="margin-bottom:1rem">{current_badge}</p>'
|
|
'<form method="POST" action="/settings/db" style="display:flex;flex-direction:column;gap:.75rem">'
|
|
'<div style="display:flex;gap:.5rem;margin-bottom:.25rem">'
|
|
f'<label style="flex:1;display:flex;align-items:center;gap:.4rem;cursor:pointer;'
|
|
f'padding:.5rem .75rem;border:1.5px solid {"#7c3aed" if not is_pg else "#cbd5e1"};'
|
|
f'border-radius:6px;font-size:.9rem">'
|
|
f'<input type="radio" name="conn_type" value="sqlite" {"checked" if not is_pg else ""}>'
|
|
f' SQLite (file locale)</label>'
|
|
f'<label style="flex:1;display:flex;align-items:center;gap:.4rem;cursor:pointer;'
|
|
f'padding:.5rem .75rem;border:1.5px solid {"#7c3aed" if is_pg else "#cbd5e1"};'
|
|
f'border-radius:6px;font-size:.9rem">'
|
|
f'<input type="radio" name="conn_type" value="postgres" {"checked" if is_pg else ""}>'
|
|
f' PostgreSQL (remoto)</label>'
|
|
'</div>'
|
|
'<fieldset id="pg-fields" style="border:1.5px solid #e2e8f0;border-radius:6px;padding:.75rem;'
|
|
f'display:{"block" if is_pg else "none"}">'
|
|
'<legend style="font-size:.8rem;font-weight:600;color:#7c3aed;padding:0 .4rem">Connessione PostgreSQL</legend>'
|
|
f'<div style="display:grid;grid-template-columns:1fr auto;gap:.5rem;margin-bottom:.5rem">'
|
|
f'<div><label {lbl}>Host</label><input type="text" name="pg_host" value="{pg_host}" {inp}></div>'
|
|
f'<div style="width:80px"><label {lbl}>Porta</label>'
|
|
f'<input type="text" name="pg_port" value="{pg_port}" {inp}></div>'
|
|
'</div>'
|
|
f'<div style="margin-bottom:.5rem"><label {lbl}>Database</label>'
|
|
f'<input type="text" name="pg_db" value="{pg_db}" {inp}></div>'
|
|
f'<div style="margin-bottom:.5rem"><label {lbl}>Utente</label>'
|
|
f'<input type="text" name="pg_user" value="{pg_user}" {inp}></div>'
|
|
f'<div><label {lbl}>Password</label>'
|
|
f'<input type="password" name="pg_password" placeholder="(invariata se vuota)" {inp}></div>'
|
|
'</fieldset>'
|
|
'<button type="submit" style="width:100%;padding:.55rem;background:#2563eb;color:#fff;border:none;'
|
|
'border-radius:6px;font-size:.9rem;font-weight:600;cursor:pointer">'
|
|
'💾 Salva e Riconnetti'
|
|
'</button>'
|
|
'</form>'
|
|
'<script>'
|
|
'(function(){'
|
|
'var radios=document.querySelectorAll(\'[name="conn_type"]\');'
|
|
'var fields=document.getElementById(\'pg-fields\');'
|
|
'radios.forEach(function(r){'
|
|
'r.addEventListener(\'change\',function(){'
|
|
'fields.style.display=this.value==="postgres"?"block":"none";'
|
|
'});});})();'
|
|
'</script>'
|
|
)
|
|
return (
|
|
'<div id="modal-db-conn" class="modal-overlay">'
|
|
'<div class="modal-box">'
|
|
'<button class="modal-close"'
|
|
' onclick="document.getElementById(\'modal-db-conn\').classList.remove(\'open\')"'
|
|
' title="Chiudi">✕</button>'
|
|
'<h3>📡 Sorgente Dati</h3>'
|
|
+ body +
|
|
'</div></div>'
|
|
)
|
|
|
|
|
|
def _build_login_modal() -> str:
|
|
enabled = _auth['enabled']
|
|
inp = (
|
|
'style="width:100%;padding:.5rem .75rem;border:1.5px solid #cbd5e1;'
|
|
'border-radius:6px;font-size:.9rem;box-sizing:border-box"'
|
|
)
|
|
lbl = 'style="font-size:.8rem;font-weight:600;color:#475569;display:block;margin-bottom:.25rem"'
|
|
if enabled:
|
|
body = (
|
|
'<p class="modal-subtitle">Stato attuale:</p>'
|
|
'<p style="margin-bottom:.9rem">'
|
|
'<span class="badge ok" style="font-size:.85rem;padding:4px 12px">✓ Login ATTIVO</span>'
|
|
'</p>'
|
|
f'<p style="font-size:.82rem;color:#64748b;margin-bottom:1.2rem">'
|
|
f'Utente: <strong>{_e(_auth["user"])}</strong></p>'
|
|
'<form method="POST" action="/settings/login" id="form-disable-login">'
|
|
'<input type="hidden" name="action" value="disable">'
|
|
'<button type="button" '
|
|
'onclick="if(confirm(\'Disabilitare il login? Tutti potranno accedere senza password.\'))'
|
|
'{document.getElementById(\'form-disable-login\').submit()}" '
|
|
'style="width:100%;padding:.55rem;background:#dc2626;color:#fff;border:none;'
|
|
'border-radius:6px;font-size:.9rem;font-weight:600;cursor:pointer">'
|
|
'Disabilita Login'
|
|
'</button>'
|
|
'</form>'
|
|
)
|
|
else:
|
|
body = (
|
|
'<p class="modal-subtitle">Stato attuale:</p>'
|
|
'<p style="margin-bottom:1rem">'
|
|
'<span class="badge err" style="font-size:.85rem;padding:4px 12px">✗ Login DISABILITATO</span>'
|
|
'</p>'
|
|
'<form method="POST" action="/settings/login" style="display:flex;flex-direction:column;gap:.75rem">'
|
|
'<input type="hidden" name="action" value="enable">'
|
|
f'<div><label {lbl}>Nuovo Utente</label><input type="text" name="new_user" required {inp}></div>'
|
|
f'<div><label {lbl}>Nuova Password</label><input type="password" name="new_password" required {inp}></div>'
|
|
'<button type="submit" style="width:100%;padding:.55rem;background:#16a34a;color:#fff;border:none;'
|
|
'border-radius:6px;font-size:.9rem;font-weight:600;cursor:pointer">'
|
|
'🔒 Abilita e Salva'
|
|
'</button>'
|
|
'</form>'
|
|
)
|
|
return (
|
|
'<div id="modal-login-settings" class="modal-overlay">'
|
|
'<div class="modal-box">'
|
|
'<button class="modal-close"'
|
|
' onclick="document.getElementById(\'modal-login-settings\').classList.remove(\'open\')"'
|
|
' title="Chiudi">✕</button>'
|
|
'<h3>🔒 Impostazioni Login</h3>'
|
|
+ body +
|
|
'</div></div>'
|
|
)
|
|
|
|
|
|
def _base(title: str, content: str, active: str, db_path: str, page_css: str = '', page_js: str = '') -> str:
|
|
now = datetime.now().strftime('%d/%m/%Y %H:%M:%S')
|
|
nav = {'nav_dashboard': '', 'nav_runs': '', 'nav_logs': '', 'nav_report': '', 'nav_schema': '', 'nav_calendar': ''}
|
|
if active in nav:
|
|
nav[active] = 'active'
|
|
|
|
db_type = _state.get('db_type', 'unknown')
|
|
if db_type == DbType.INTRAZ:
|
|
h1_title = 'RPA — Corsi Intraziendali'
|
|
else:
|
|
h1_title = 'RPA — Comunicazioni Regione Lombardia'
|
|
|
|
if db_type == DbType.INTRAZ:
|
|
cls = 'active' if active == 'nav_iscrizioni' else ''
|
|
nav_step_link = f'<a href="/steps" class="{cls}">RPA Steps</a>'
|
|
cls_api_isc = 'active' if active == 'nav_api_iscrizioni' else ''
|
|
nav_report_link = f'<a href="/iscrizioni-api" class="{cls_api_isc}">Iscrizioni</a>'
|
|
cls_sp = 'active' if active == 'nav_sharepoint' else ''
|
|
nav_sharepoint_link = f'<a href="/sharepoint" class="{cls_sp}">SharePoint</a>'
|
|
cls_email = 'active' if active == 'nav_email' else ''
|
|
nav_pec_link = f'<a href="/email" class="{cls_email}">Email</a>'
|
|
else:
|
|
cls = 'active' if active == 'nav_documenti' else ''
|
|
nav_step_link = f'<a href="/documenti" class="{cls}">Documenti</a>'
|
|
cls_pec = 'active' if active == 'nav_pec' else ''
|
|
nav_pec_link = f'<a href="/pec" class="{cls_pec}">PEC</a>'
|
|
cls_rep = nav.get('nav_report', '')
|
|
nav_report_link = f'<a href="/report" class="{cls_rep}">Report</a>'
|
|
nav_sharepoint_link = ''
|
|
|
|
if _conn['type'] == ConnType.POSTGRES:
|
|
db_display = _e(f"{_conn['pg_host']}:{_conn['pg_port']}/{_conn['pg_db']}")
|
|
else:
|
|
db_display = _e(Path(db_path).name) if db_path else '-'
|
|
|
|
return _tmpl('_base.html').substitute(
|
|
css=_css(), page_css=page_css, page_js=page_js, title=title, now=now,
|
|
db_name=db_display,
|
|
h1_title=h1_title,
|
|
content=content,
|
|
nav_step_link=nav_step_link,
|
|
nav_report_link=nav_report_link,
|
|
nav_sharepoint_link=nav_sharepoint_link,
|
|
nav_pec_link=nav_pec_link,
|
|
modal_login=_build_login_modal(),
|
|
modal_db_conn=_build_db_conn_modal(),
|
|
logout_btn='<a href="/logout" class="logout-btn" title="Esci">🔒 Esci</a>' if _auth['enabled'] else '',
|
|
**nav,
|
|
)
|