Work-order codes no longer overlap (#42), Materials before Heat map (#44), 60 min idle for Admin and Clerical (#45)
- Traveler: each work-order line shrinks, then clips, to fit its own box; 18 pt gap between boxes - Work orders tab: card text wraps inside the card, smaller work order, wider cards - Packet tabs: Materials before Heat map - Sessions: SESSION_DESK_MINUTES (60) for Admin and Clerical lock and sign-out; /me answers lock_minutes and the browser's idle lock follows it Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -66,6 +66,7 @@ def me_payload(conn: Any, p: Principal) -> dict[str, Any]:
|
||||
"expires_at": s["expires_at"],
|
||||
"locked": s.get("locked_since") is not None,
|
||||
"locked_since": s.get("locked_since"),
|
||||
"lock_minutes": p.lock_minutes,
|
||||
"device": devices.public(p.device),
|
||||
},
|
||||
"pin": pins.state(conn, p.user_id).public(),
|
||||
|
||||
@@ -28,6 +28,18 @@ log = logging.getLogger(__name__)
|
||||
LAST_SEEN_SLACK = timedelta(seconds=30)
|
||||
|
||||
|
||||
def desk_roles(roles: list[str]) -> bool:
|
||||
"""Admin and Clerical get the longer desk idle limits."""
|
||||
return any(r == "admin" or r.startswith("clerical") for r in roles)
|
||||
|
||||
|
||||
def idle_minutes(s: Settings, roles: list[str]) -> tuple[int, int]:
|
||||
"""(sign-out, screen lock) idle minutes for a session with these roles."""
|
||||
if desk_roles(roles):
|
||||
return max(s.session_idle_minutes, s.session_desk_minutes), max(s.session_lock_minutes, s.session_desk_minutes)
|
||||
return s.session_idle_minutes, s.session_lock_minutes
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- plumbing
|
||||
|
||||
|
||||
@@ -160,6 +172,7 @@ class Principal:
|
||||
kind: str
|
||||
device: dict[str, Any] | None
|
||||
ip: str | None
|
||||
lock_minutes: int = 3
|
||||
|
||||
@property
|
||||
def session_id(self) -> uuid.UUID:
|
||||
@@ -253,11 +266,12 @@ def resolve(request: Request, d: Db, *, allow_locked: bool, token: str | None =
|
||||
raise ApiError(401, "no_session", "Not signed in.")
|
||||
now = row["db_now"]
|
||||
idle = now - row["last_seen_at"]
|
||||
if idle > timedelta(minutes=s.session_idle_minutes):
|
||||
idle_min, lock_min = idle_minutes(s, list((row.get("roles_snapshot") or {}).get("roles", [])))
|
||||
if idle > timedelta(minutes=idle_min):
|
||||
_expire(conn, row, "idle", ip)
|
||||
raise ApiError(401, "session_expired", "Signed out after a period of no activity.")
|
||||
lock_source: str | None = None
|
||||
if row["locked_since"] is None and idle > timedelta(minutes=s.session_lock_minutes):
|
||||
if row["locked_since"] is None and idle > timedelta(minutes=lock_min):
|
||||
lock_source = "idle"
|
||||
hidden_since = row.get("hidden_since")
|
||||
if hidden_since is not None:
|
||||
@@ -308,6 +322,7 @@ def resolve(request: Request, d: Db, *, allow_locked: bool, token: str | None =
|
||||
kind=str(row["kind"]),
|
||||
device=device,
|
||||
ip=ip,
|
||||
lock_minutes=idle_minutes(s, roles)[1],
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user