This commit is contained in:
parent
f3b91ac75b
commit
782bcec5bc
@ -185,7 +185,7 @@ Online status is a single **`users.last_seen`** UTC-ISO column (ensured in `data
|
||||
|
||||
**Write path (all workers):** `main.py`'s `track_presence` HTTP middleware resolves the cached current user on every non-`/static`, non-`/avatar` request and calls `presence.touch(uid)`. `touch` keeps a per-worker in-memory `_last_write: dict[uid -> monotonic]` and writes `users.last_seen` (via `database.set_last_seen`) only when the last write for that uid is older than `config.PRESENCE_WRITE_SECONDS` (= `PRESENCE_TIMEOUT_SECONDS // 2`). So continuous browsing is a dict lookup; a write happens at most ~once per half-window per active user per worker, and the row is updated in place (zero growth). It deliberately does **not** call `clear_user_cache` (that would defeat the 300s auth cache; the stale cached self-row is irrelevant since presence of *other* users is always read from a fresh row).
|
||||
|
||||
**Consent gate (write path).** `touch` checks `presence.recording_allowed(uid)` (the `activity_recording` consent) **after** the per-worker throttle, so the consent read costs at most one query per half-window per active user rather than one per request. A user who withdraws the consent simply stops being written and appears offline; `base.html` shows a `.recording-indicator` while it is on. Never move the check above the throttle.
|
||||
**Consent gate (write path).** `touch` checks `presence.recording_allowed(uid)` (the `activity_recording` consent) **after** the per-worker throttle, so the consent read costs at most one query per half-window per active user rather than one per request. A user who withdraws the consent simply stops being written and appears offline. Never move the check above the throttle.
|
||||
|
||||
**Read path (any worker):** `presence.is_online(user_row)` = `now - last_seen < PRESENCE_TIMEOUT_SECONDS` (env `DEVPLACE_PRESENCE_TIMEOUT_SECONDS`, default 60). Profile (`routers/profile/index.py` -> `profile_online`) and messages (`routers/messages.py` seed) read `last_seen` off the user row they already loaded - no extra query. Exposed as the Jinja global `is_online(user)` (`templating.py`), on `UserOut.last_seen` and `ProfileOut.profile_online`. This is the **only** cross-worker-correct approach here because pub/sub is in-process.
|
||||
|
||||
|
||||
@ -106,7 +106,7 @@ The gate is at exactly one place: `GatewayService.consent_denied` in `services/o
|
||||
|
||||
`container_credentials` gates `containers/api.validate_run_as`: a container configured to run as **someone else** would inject that person's real `DEVPLACE_API_KEY` into software they do not operate, so it is refused unless they granted the consent. Running a container as yourself never asks - you are the one handing over your own credential.
|
||||
|
||||
`activity_recording` gates `presence.touch`, checked **after** the per-worker throttle so the consent read costs at most one query per half-window per user. Withdraw it and you simply appear offline. The indicator is the `.recording-indicator` in `base.html`, rendered from the `activity_recording_on(user)` Jinja global.
|
||||
`activity_recording` gates `presence.touch`, checked **after** the per-worker throttle so the consent read costs at most one query per half-window per user. Withdraw it and you simply appear offline. The consent is managed on the profile privacy tab; no page chrome advertises its state.
|
||||
|
||||
## Terms re-acceptance
|
||||
|
||||
|
||||
@ -1375,35 +1375,6 @@ body:has(.page-messages) {
|
||||
}
|
||||
|
||||
|
||||
.recording-indicator {
|
||||
position: fixed;
|
||||
left: var(--space-lg);
|
||||
bottom: calc(var(--space-2xl) + var(--space-2xl));
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--space-xs);
|
||||
padding: var(--space-xs) var(--space-sm);
|
||||
border-radius: var(--radius);
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border-light);
|
||||
color: var(--text-muted);
|
||||
font-size: 0.75rem;
|
||||
z-index: var(--z-fab);
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.recording-indicator a {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.recording-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: var(--danger);
|
||||
flex: none;
|
||||
}
|
||||
|
||||
.maturity-gate {
|
||||
padding: var(--space-2xl);
|
||||
text-align: center;
|
||||
|
||||
@ -212,13 +212,6 @@
|
||||
</footer>
|
||||
{% endblock %}
|
||||
|
||||
{% if user and activity_recording_on(user) %}
|
||||
<div class="recording-indicator" role="status" aria-live="off" title="Your presence and session activity are recorded while you are signed in">
|
||||
<span class="recording-dot" aria-hidden="true"></span>
|
||||
<a href="/profile/{{ user['username'] }}?tab=privacy">Activity recording on</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{%- set _response_time = response_time_ms(request) -%}
|
||||
{%- if _response_time %}<div class="response-time-indicator" title="Server response time" aria-hidden="true">{{ _response_time }} ms</div>{% endif -%}
|
||||
|
||||
|
||||
@ -58,13 +58,6 @@ templates.env.globals["is_self"] = is_self
|
||||
templates.env.globals["guest_disabled"] = guest_disabled
|
||||
templates.env.globals["is_online"] = presence.is_online
|
||||
|
||||
|
||||
def activity_recording_on(user) -> bool:
|
||||
return bool(user) and presence.recording_allowed(user.get("uid", ""))
|
||||
|
||||
|
||||
templates.env.globals["activity_recording_on"] = activity_recording_on
|
||||
|
||||
from devplacepy.docs_devrant import devrant_endpoints
|
||||
|
||||
templates.env.globals["devrant_endpoints"] = devrant_endpoints
|
||||
|
||||
Loading…
Reference in New Issue
Block a user