271 lines
12 KiB
Python
271 lines
12 KiB
Python
"""Router contacts — gestion des responsables applicatifs et scopes"""
|
|
from fastapi import APIRouter, Request, Depends, Query, Form
|
|
from fastapi.responses import HTMLResponse, RedirectResponse
|
|
from fastapi.templating import Jinja2Templates
|
|
from sqlalchemy import text
|
|
from ..dependencies import get_db, get_current_user, get_user_perms, can_view, can_edit, base_context
|
|
from ..config import APP_NAME
|
|
|
|
router = APIRouter()
|
|
templates = Jinja2Templates(directory="app/templates")
|
|
|
|
ROLES = [
|
|
("responsable_domaine", "Responsable domaine"),
|
|
("responsable_prod", "Responsable production"),
|
|
("responsable_applicatif", "Responsable applicatif"),
|
|
("referent_technique", "Référent technique"),
|
|
("ra_prod", "RA Production"),
|
|
("ra_recette", "RA Recette"),
|
|
("ra_preprod", "RA Pré-prod"),
|
|
("ra_test", "RA Test"),
|
|
("ra_dev", "RA Développement"),
|
|
("chef_projet", "Chef de projet"),
|
|
("contact_technique", "Contact technique"),
|
|
("editeur", "Éditeur"),
|
|
("autre", "Autre"),
|
|
]
|
|
|
|
SCOPE_TYPES = [
|
|
("domain", "Domaine"),
|
|
("application", "Application"),
|
|
("app_group", "Groupe applicatif"),
|
|
("server", "Serveur"),
|
|
("zone", "Zone"),
|
|
]
|
|
|
|
ENV_SCOPES = ["all", "prod", "recette", "preprod", "test", "dev"]
|
|
|
|
|
|
@router.get("/contacts", response_class=HTMLResponse)
|
|
async def contacts_page(request: Request, db=Depends(get_db),
|
|
search: str = Query(None), role: str = Query(None),
|
|
server: str = Query(None)):
|
|
user = get_current_user(request)
|
|
if not user:
|
|
return RedirectResponse(url="/login")
|
|
perms = get_user_perms(db, user)
|
|
if not can_view(perms, "servers"):
|
|
return RedirectResponse(url="/dashboard")
|
|
|
|
where = ["1=1"]
|
|
params = {}
|
|
if search:
|
|
where.append("(c.name ILIKE :q OR c.email ILIKE :q)")
|
|
params["q"] = f"%{search}%"
|
|
if role:
|
|
where.append("c.role = :role")
|
|
params["role"] = role
|
|
|
|
# Recherche par serveur : trouver les contacts liés à ce serveur
|
|
server_info = None
|
|
if server:
|
|
server_info = db.execute(text("""
|
|
SELECT s.id, s.hostname, d.code as domain_code, d.name as domain_name,
|
|
e.name as env_name, s.app_group, z.name as zone_name,
|
|
ss.app_type
|
|
FROM servers s
|
|
LEFT JOIN domain_environments de ON s.domain_env_id = de.id
|
|
LEFT JOIN domains d ON de.domain_id = d.id
|
|
LEFT JOIN environments e ON de.environment_id = e.id
|
|
LEFT JOIN zones z ON s.zone_id = z.id
|
|
LEFT JOIN server_specifics ss ON ss.server_id = s.id
|
|
WHERE LOWER(s.hostname) = LOWER(:h)
|
|
"""), {"h": server.strip()}).fetchone()
|
|
|
|
if server_info:
|
|
where.append("""c.id IN (
|
|
SELECT cs.contact_id FROM contact_scopes cs WHERE
|
|
(cs.scope_type = 'server' AND LOWER(cs.scope_value) = LOWER(:srv_hn))
|
|
OR (cs.scope_type = 'domain' AND cs.scope_value = :srv_dom)
|
|
OR (cs.scope_type = 'app_group' AND cs.scope_value = :srv_ag)
|
|
OR (cs.scope_type = 'application' AND UPPER(cs.scope_value) = UPPER(:srv_app))
|
|
OR (cs.scope_type = 'zone' AND cs.scope_value = :srv_zone)
|
|
)""")
|
|
params["srv_hn"] = server.strip()
|
|
params["srv_dom"] = server_info.domain_code or ""
|
|
params["srv_ag"] = server_info.app_group or ""
|
|
params["srv_app"] = server_info.app_type or ""
|
|
params["srv_zone"] = server_info.zone_name or ""
|
|
|
|
wc = " AND ".join(where)
|
|
|
|
contacts = db.execute(text(f"""
|
|
SELECT c.*,
|
|
(SELECT string_agg(cs.scope_type || ':' || cs.scope_value ||
|
|
CASE WHEN cs.env_scope != 'all' THEN '(' || cs.env_scope || ')' ELSE '' END, ', '
|
|
ORDER BY cs.scope_type, cs.scope_value)
|
|
FROM contact_scopes cs WHERE cs.contact_id = c.id) as scopes_summary
|
|
FROM contacts c WHERE {wc} ORDER BY c.name
|
|
"""), params).fetchall()
|
|
|
|
roles_in_db = db.execute(text(
|
|
"SELECT DISTINCT role FROM contacts ORDER BY role"
|
|
)).fetchall()
|
|
|
|
domains = db.execute(text("SELECT code, name FROM domains ORDER BY display_order")).fetchall()
|
|
app_types = db.execute(text(
|
|
"SELECT DISTINCT app_type FROM server_specifics WHERE app_type IS NOT NULL ORDER BY app_type"
|
|
)).fetchall()
|
|
|
|
perms = get_user_perms(db, user)
|
|
ctx = base_context(request, db, user)
|
|
ctx.update({
|
|
"app_name": APP_NAME, "contacts": contacts,
|
|
"roles": ROLES, "roles_in_db": [r.role for r in roles_in_db],
|
|
"scope_types": SCOPE_TYPES, "env_scopes": ENV_SCOPES,
|
|
"domains": domains, "app_types": [r.app_type for r in app_types],
|
|
"search": search, "role_filter": role, "server": server, "server_info": server_info,
|
|
"msg": request.query_params.get("msg"),
|
|
"can_edit_contacts": can_edit(perms, "servers") or can_edit(perms, "contacts"),
|
|
})
|
|
return templates.TemplateResponse("contacts.html", ctx)
|
|
|
|
|
|
@router.get("/contacts/{contact_id}", response_class=HTMLResponse)
|
|
async def contact_detail(request: Request, contact_id: int, db=Depends(get_db)):
|
|
user = get_current_user(request)
|
|
if not user:
|
|
return HTMLResponse("<p>Non autorisé</p>")
|
|
|
|
contact = db.execute(text("SELECT * FROM contacts WHERE id = :id"),
|
|
{"id": contact_id}).fetchone()
|
|
if not contact:
|
|
return HTMLResponse("<p>Non trouvé</p>")
|
|
|
|
scopes = db.execute(text("""
|
|
SELECT cs.* FROM contact_scopes cs WHERE cs.contact_id = :cid ORDER BY cs.scope_type, cs.scope_value
|
|
"""), {"cid": contact_id}).fetchall()
|
|
|
|
# Serveurs liés via scopes
|
|
servers = db.execute(text("""
|
|
SELECT DISTINCT s.id, s.hostname, d.name as domaine, e.name as environnement
|
|
FROM servers s
|
|
LEFT JOIN domain_environments de ON s.domain_env_id = de.id
|
|
LEFT JOIN domains d ON de.domain_id = d.id
|
|
LEFT JOIN environments e ON de.environment_id = e.id
|
|
LEFT JOIN server_specifics ss ON ss.server_id = s.id
|
|
LEFT JOIN zones z ON s.zone_id = z.id
|
|
WHERE EXISTS (
|
|
SELECT 1 FROM contact_scopes cs WHERE cs.contact_id = :cid AND (
|
|
(cs.scope_type = 'domain' AND d.code = cs.scope_value)
|
|
OR (cs.scope_type = 'application' AND UPPER(ss.app_type) = UPPER(cs.scope_value))
|
|
OR (cs.scope_type = 'server' AND LOWER(s.hostname) = LOWER(cs.scope_value))
|
|
OR (cs.scope_type = 'app_group' AND s.app_group = cs.scope_value)
|
|
OR (cs.scope_type = 'zone' AND z.name = cs.scope_value)
|
|
)
|
|
)
|
|
ORDER BY d.name, s.hostname LIMIT 100
|
|
"""), {"cid": contact_id}).fetchall()
|
|
|
|
domains = db.execute(text("SELECT code, name FROM domains ORDER BY display_order")).fetchall()
|
|
app_types = db.execute(text(
|
|
"SELECT DISTINCT app_type FROM server_specifics WHERE app_type IS NOT NULL ORDER BY app_type"
|
|
)).fetchall()
|
|
|
|
return templates.TemplateResponse("partials/contact_detail.html", {
|
|
"request": request, "c": contact, "scopes": scopes, "servers": servers,
|
|
"roles": ROLES, "scope_types": SCOPE_TYPES, "env_scopes": ENV_SCOPES,
|
|
"domains": domains, "app_types": [r.app_type for r in app_types],
|
|
})
|
|
|
|
|
|
@router.post("/contacts/add")
|
|
async def contact_add(request: Request, db=Depends(get_db),
|
|
name: str = Form(...), email: str = Form(...), contact_role: str = Form("autre")):
|
|
user = get_current_user(request)
|
|
if not user:
|
|
return RedirectResponse(url="/login")
|
|
perms = get_user_perms(db, user)
|
|
if not can_edit(perms, "servers"):
|
|
return RedirectResponse(url="/contacts")
|
|
try:
|
|
db.execute(text("""
|
|
INSERT INTO contacts (name, email, role, is_active)
|
|
VALUES (:n, :e, :r, true)
|
|
"""), {"n": name.strip(), "e": email.strip().lower(), "r": contact_role})
|
|
db.commit()
|
|
return RedirectResponse(url="/contacts?msg=added", status_code=303)
|
|
except Exception:
|
|
db.rollback()
|
|
return RedirectResponse(url="/contacts?msg=exists", status_code=303)
|
|
|
|
|
|
@router.post("/contacts/{contact_id}/edit")
|
|
async def contact_edit(request: Request, contact_id: int, db=Depends(get_db),
|
|
name: str = Form(""), email: str = Form(""), contact_role: str = Form("")):
|
|
user = get_current_user(request)
|
|
if not user:
|
|
return RedirectResponse(url="/login")
|
|
perms = get_user_perms(db, user)
|
|
if not can_edit(perms, "servers"):
|
|
return RedirectResponse(url="/contacts")
|
|
updates = []; params = {"id": contact_id}
|
|
if name: updates.append("name = :n"); params["n"] = name
|
|
if email: updates.append("email = :e"); params["e"] = email.lower()
|
|
if contact_role: updates.append("role = :r"); params["r"] = contact_role
|
|
if updates:
|
|
db.execute(text(f"UPDATE contacts SET {', '.join(updates)} WHERE id = :id"), params)
|
|
db.commit()
|
|
return RedirectResponse(url="/contacts?msg=edited", status_code=303)
|
|
|
|
|
|
@router.post("/contacts/{contact_id}/toggle")
|
|
async def contact_toggle(request: Request, contact_id: int, db=Depends(get_db)):
|
|
user = get_current_user(request)
|
|
if not user:
|
|
return RedirectResponse(url="/login")
|
|
perms = get_user_perms(db, user)
|
|
if not can_edit(perms, "servers"):
|
|
return RedirectResponse(url="/contacts")
|
|
db.execute(text("UPDATE contacts SET is_active = NOT is_active WHERE id = :id"), {"id": contact_id})
|
|
db.commit()
|
|
return RedirectResponse(url="/contacts?msg=toggled", status_code=303)
|
|
|
|
|
|
@router.post("/contacts/{contact_id}/scope/add")
|
|
async def scope_add(request: Request, contact_id: int, db=Depends(get_db),
|
|
scope_type: str = Form(...), scope_value: str = Form(...),
|
|
env_scope: str = Form("all")):
|
|
user = get_current_user(request)
|
|
if not user:
|
|
return RedirectResponse(url="/login")
|
|
perms = get_user_perms(db, user)
|
|
if not can_edit(perms, "servers"):
|
|
return RedirectResponse(url="/contacts")
|
|
try:
|
|
db.execute(text("""
|
|
INSERT INTO contact_scopes (contact_id, scope_type, scope_value, env_scope)
|
|
VALUES (:cid, :st, :sv, :es)
|
|
"""), {"cid": contact_id, "st": scope_type, "sv": scope_value.strip(), "es": env_scope})
|
|
db.commit()
|
|
except Exception:
|
|
db.rollback()
|
|
return RedirectResponse(url=f"/contacts?msg=scope_added", status_code=303)
|
|
|
|
|
|
@router.post("/contacts/scope/{scope_id}/delete")
|
|
async def scope_delete(request: Request, scope_id: int, db=Depends(get_db)):
|
|
user = get_current_user(request)
|
|
if not user:
|
|
return RedirectResponse(url="/login")
|
|
perms = get_user_perms(db, user)
|
|
if not can_edit(perms, "servers"):
|
|
return RedirectResponse(url="/contacts")
|
|
db.execute(text("DELETE FROM contact_scopes WHERE id = :id"), {"id": scope_id})
|
|
db.commit()
|
|
return RedirectResponse(url="/contacts?msg=scope_deleted", status_code=303)
|
|
|
|
|
|
@router.post("/contacts/{contact_id}/delete")
|
|
async def contact_delete(request: Request, contact_id: int, db=Depends(get_db)):
|
|
user = get_current_user(request)
|
|
if not user:
|
|
return RedirectResponse(url="/login")
|
|
perms = get_user_perms(db, user)
|
|
if not can_edit(perms, "servers"):
|
|
return RedirectResponse(url="/contacts")
|
|
db.execute(text("DELETE FROM contact_scopes WHERE contact_id = :cid"), {"cid": contact_id})
|
|
db.execute(text("DELETE FROM contacts WHERE id = :cid"), {"cid": contact_id})
|
|
db.commit()
|
|
return RedirectResponse(url="/contacts?msg=deleted", status_code=303)
|