Corrige le rendu de la page mot de passe, masque la nav si non connecté,
réduit le score spam des emails, et rend le niveau de log configurable - Les styles .login-* étaient définis uniquement dans login.html, donc set_password.html s'affichait sans aucun style. Déplacé dans base.html. - La nav affichait les liens même sans session active. - mail.py ajoute Message-ID/Date et encode en quoted-printable (au lieu de base64) pour réduire le score spam (MISSING_MID, MISSING_DATE, MIME_BASE64_TEXT). - log_level devient une directive de config.json plutôt qu'une variable d'environnement. - README: section démarrage rapide (copie de config.example.json, compose.override.yml pour les surcharges locales).
This commit is contained in:
@ -1,7 +1,8 @@
|
||||
"""Mail delivery via a configured SMTP relay (host/port/credentials in config.json)."""
|
||||
import logging
|
||||
import smtplib
|
||||
from email.mime.text import MIMEText
|
||||
from email.message import EmailMessage
|
||||
from email.utils import make_msgid, formatdate
|
||||
|
||||
logger = logging.getLogger("pointeuse.mail")
|
||||
|
||||
@ -16,10 +17,13 @@ def send_mail(to_addr: str, subject: str, body: str, from_addr: str, smtp_cfg: d
|
||||
password = smtp_cfg.get("password")
|
||||
use_tls = smtp_cfg.get("use_tls", True)
|
||||
|
||||
msg = MIMEText(body)
|
||||
msg = EmailMessage()
|
||||
msg.set_content(body, cte="quoted-printable") # évite MIME_BASE64_TEXT
|
||||
msg["Subject"] = subject
|
||||
msg["From"] = from_addr
|
||||
msg["To"] = to_addr
|
||||
msg["Message-ID"] = make_msgid(domain=from_addr.split("@", 1)[1])
|
||||
msg["Date"] = formatdate(localtime=True)
|
||||
|
||||
logger.info(
|
||||
"envoi mail: to=%r from=%r subject=%r via smtp=%s:%s user=%r tls=%r",
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import asyncio
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import secrets
|
||||
from datetime import datetime, timedelta
|
||||
@ -26,7 +25,7 @@ from classement import compute_classement
|
||||
from notifications import reminder_loop
|
||||
|
||||
logging.basicConfig(
|
||||
level=getattr(logging, os.environ.get("LOG_LEVEL", "DEBUG").upper(), logging.DEBUG),
|
||||
level=getattr(logging, load_config().get("log_level", "DEBUG").upper(), logging.DEBUG),
|
||||
format="%(asctime)s %(levelname)s %(name)s: %(message)s",
|
||||
)
|
||||
logger = logging.getLogger("pointeuse")
|
||||
|
||||
@ -8,6 +8,7 @@ CONFIG_FILE = DATA_DIR / "config.json"
|
||||
|
||||
DEFAULT_CONFIG = {
|
||||
"heures_jour": 7.8, # 7h48 = 39h/semaine
|
||||
"log_level": "DEBUG", # DEBUG, INFO, WARNING ou ERROR
|
||||
"allowed_email_domain": "", # ex: "sdv.fr" — seules ces adresses peuvent se connecter
|
||||
"mail_from": "", # ex: "no-reply@sdv.fr" — expéditeur des emails de connexion
|
||||
"smtp": {
|
||||
|
||||
@ -840,6 +840,82 @@
|
||||
50% { transform: scale(1.25); }
|
||||
}
|
||||
|
||||
/* ── Login / set-password ── */
|
||||
.login-wrap {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
padding-top: 4rem;
|
||||
}
|
||||
.login-card {
|
||||
background: var(--surface);
|
||||
border: 1.5px solid var(--rule);
|
||||
padding: 2.5rem 3rem;
|
||||
width: 380px;
|
||||
max-width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
.login-header { display: flex; flex-direction: column; gap: .35rem; }
|
||||
.login-eyebrow {
|
||||
font-family: var(--mono);
|
||||
font-size: 10px;
|
||||
letter-spacing: .12em;
|
||||
text-transform: uppercase;
|
||||
color: var(--muted);
|
||||
}
|
||||
.login-title {
|
||||
font-family: var(--mono);
|
||||
font-size: 22px;
|
||||
font-weight: 500;
|
||||
color: var(--text);
|
||||
letter-spacing: -.01em;
|
||||
}
|
||||
.login-error {
|
||||
background: var(--neg-bg);
|
||||
border-left: 3px solid var(--neg);
|
||||
color: var(--neg);
|
||||
font-size: 12px;
|
||||
padding: .6rem .9rem;
|
||||
font-family: var(--mono);
|
||||
}
|
||||
.login-form { display: flex; flex-direction: column; gap: .75rem; }
|
||||
.login-label {
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
letter-spacing: .06em;
|
||||
text-transform: uppercase;
|
||||
color: var(--muted);
|
||||
}
|
||||
.login-input {
|
||||
font-family: var(--mono);
|
||||
font-size: 15px;
|
||||
padding: .6rem .75rem;
|
||||
border: 1.5px solid var(--rule);
|
||||
background: var(--ground);
|
||||
color: var(--text);
|
||||
outline: none;
|
||||
transition: border-color .15s;
|
||||
}
|
||||
.login-input:focus { border-color: var(--accent); }
|
||||
.login-hint { font-size: 11px; color: var(--muted); }
|
||||
.login-btn {
|
||||
margin-top: .5rem;
|
||||
font-family: var(--mono);
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
letter-spacing: .08em;
|
||||
text-transform: uppercase;
|
||||
padding: .7rem 1.5rem;
|
||||
background: var(--text);
|
||||
color: var(--surface);
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: background .15s;
|
||||
}
|
||||
.login-btn:hover { background: var(--accent); }
|
||||
|
||||
/* ── Chart zoom modal ── */
|
||||
.chart-modal {
|
||||
position: fixed;
|
||||
@ -928,11 +1004,11 @@
|
||||
<nav>
|
||||
<a href="/" class="nav-brand"><span class="nav-dot"></span>Décompte Horaire</a>
|
||||
<span class="spacer"></span>
|
||||
{% if current_user is defined and current_user %}
|
||||
<a href="/" class="nav-link">Semaine courante</a>
|
||||
<a href="/stats" class="nav-link">Statistiques</a>
|
||||
<a href="/classement" class="nav-link">Classement</a>
|
||||
<a href="/settings" class="nav-link">Réglages</a>
|
||||
{% if current_user is defined and current_user %}
|
||||
<span class="nav-user">{{ current_user }}</span>
|
||||
<a href="/logout" class="nav-link nav-logout">Déconnexion</a>
|
||||
{% endif %}
|
||||
|
||||
@ -53,81 +53,4 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.login-wrap {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
padding-top: 4rem;
|
||||
}
|
||||
.login-card {
|
||||
background: var(--surface);
|
||||
border: 1.5px solid var(--rule);
|
||||
padding: 2.5rem 3rem;
|
||||
width: 380px;
|
||||
max-width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
.login-header { display: flex; flex-direction: column; gap: .35rem; }
|
||||
.login-eyebrow {
|
||||
font-family: var(--mono);
|
||||
font-size: 10px;
|
||||
letter-spacing: .12em;
|
||||
text-transform: uppercase;
|
||||
color: var(--muted);
|
||||
}
|
||||
.login-title {
|
||||
font-family: var(--mono);
|
||||
font-size: 22px;
|
||||
font-weight: 500;
|
||||
color: var(--text);
|
||||
letter-spacing: -.01em;
|
||||
}
|
||||
.login-error {
|
||||
background: var(--neg-bg);
|
||||
border-left: 3px solid var(--neg);
|
||||
color: var(--neg);
|
||||
font-size: 12px;
|
||||
padding: .6rem .9rem;
|
||||
font-family: var(--mono);
|
||||
}
|
||||
.login-form { display: flex; flex-direction: column; gap: .75rem; }
|
||||
.login-label {
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
letter-spacing: .06em;
|
||||
text-transform: uppercase;
|
||||
color: var(--muted);
|
||||
}
|
||||
.login-input {
|
||||
font-family: var(--mono);
|
||||
font-size: 15px;
|
||||
padding: .6rem .75rem;
|
||||
border: 1.5px solid var(--rule);
|
||||
background: var(--ground);
|
||||
color: var(--text);
|
||||
outline: none;
|
||||
transition: border-color .15s;
|
||||
}
|
||||
.login-input:focus { border-color: var(--accent); }
|
||||
.login-hint { font-size: 11px; color: var(--muted); }
|
||||
.login-btn {
|
||||
margin-top: .5rem;
|
||||
font-family: var(--mono);
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
letter-spacing: .08em;
|
||||
text-transform: uppercase;
|
||||
padding: .7rem 1.5rem;
|
||||
background: var(--text);
|
||||
color: var(--surface);
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: background .15s;
|
||||
}
|
||||
.login-btn:hover { background: var(--accent); }
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user