127 lines
4.3 KiB
Python
127 lines
4.3 KiB
Python
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()
|