from __future__ import annotations from pathlib import Path from fastapi import APIRouter, Request from fastapi.responses import FileResponse, JSONResponse, RedirectResponse from app.core.db.common import CommonSessionLocal from app.core.security.session_auth import SESSION_USER_ID_KEY, get_current_user from app.modules.core.rbac.permission_guard import require_permission router = APIRouter(tags=["pwa-ui"]) PWA_REDIRECT_SESSION_KEY = "pending_post_login_redirect" MOBILE_ATTENDANCE_PATH = "/mobile/attendance" PWA_STATIC_ROOT = Path("app/ui/static/pwa") def _safe_mobile_redirect_path(value: str | None) -> str | None: value = (value or "").strip() if value in {MOBILE_ATTENDANCE_PATH, "/employee/attendance", "/employee/dashboard"}: return value return None def _remember_mobile_attendance_redirect(request: Request) -> None: request.session[PWA_REDIRECT_SESSION_KEY] = MOBILE_ATTENDANCE_PATH @router.get("/manifest.webmanifest", include_in_schema=False) def pwa_manifest(): return JSONResponse( { "name": "ARRR ERP Attendance", "short_name": "ARRR ERP", "description": "Mobile attendance access for ARRR ERP staff users.", "id": "/mobile/attendance", "start_url": "/mobile/attendance", "scope": "/", "display": "standalone", "orientation": "portrait", "theme_color": "#1e3a8a", "background_color": "#f8fafc", "categories": ["business", "productivity"], "icons": [ { "src": "/static/pwa/icons/icon-192.png", "sizes": "192x192", "type": "image/png", "purpose": "any", }, { "src": "/static/pwa/icons/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any", }, { "src": "/static/pwa/icons/maskable-512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable", }, ], "shortcuts": [ { "name": "Mark Attendance", "short_name": "Attendance", "description": "Open mobile attendance punch screen.", "url": MOBILE_ATTENDANCE_PATH, "icons": [ { "src": "/static/pwa/icons/icon-192.png", "sizes": "192x192", "type": "image/png", } ], } ], }, media_type="application/manifest+json", headers={"Cache-Control": "no-cache"}, ) @router.get("/sw.js", include_in_schema=False) def pwa_service_worker(): return FileResponse( PWA_STATIC_ROOT / "sw.js", media_type="application/javascript", headers={ "Cache-Control": "no-cache", "Service-Worker-Allowed": "/", }, ) @router.get("/mobile", include_in_schema=False) def mobile_root(request: Request): return RedirectResponse(url=MOBILE_ATTENDANCE_PATH, status_code=303) @router.get(MOBILE_ATTENDANCE_PATH, include_in_schema=False) def mobile_attendance(request: Request): if not request.session.get(SESSION_USER_ID_KEY): _remember_mobile_attendance_redirect(request) return RedirectResponse(url="/login", status_code=303) if not request.session.get("otp_verified", False): _remember_mobile_attendance_redirect(request) return RedirectResponse(url="/otp", status_code=303) db = CommonSessionLocal() try: current_user = get_current_user(request, db=db) if not current_user: request.session.clear() _remember_mobile_attendance_redirect(request) return RedirectResponse(url="/login", status_code=303) try: require_permission(db, current_user, "employees.attendance.view_self") except Exception: return RedirectResponse(url="/employee/dashboard", status_code=303) return RedirectResponse(url="/employee/attendance", status_code=303) finally: db.close()