Skip to content

Commit e460b45

Browse files
committed
Last active at timestamp for machines
1 parent b0839a2 commit e460b45

8 files changed

Lines changed: 40 additions & 2 deletions

File tree

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
"""add machines.last_active_at
2+
3+
Revision ID: 0005
4+
Revises: 0004
5+
Create Date: 2026-03-24 00:00:00.000000
6+
"""
7+
from alembic import op
8+
import sqlalchemy as sa
9+
10+
revision = "0005"
11+
down_revision = "0004"
12+
branch_labels = None
13+
depends_on = None
14+
15+
16+
def upgrade() -> None:
17+
op.add_column("machines", sa.Column("last_active_at", sa.DateTime(), nullable=True))
18+
19+
20+
def downgrade() -> None:
21+
op.drop_column("machines", "last_active_at")

app/auth/deps.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from datetime import UTC, datetime
2+
13
from fastapi import Depends, HTTPException, Request
24
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
35
from sqlalchemy.orm import Session
@@ -66,6 +68,9 @@ def get_current_device(
6668
)
6769
for m in machine:
6870
if verify_api_token(token, m.api_token_hash):
71+
m.last_active_at = datetime.now(UTC).replace(tzinfo=None)
72+
db.commit()
73+
db.refresh(m)
6974
return m
7075
raise HTTPException(status_code=401, detail="Invalid or revoked API token")
7176

@@ -100,6 +105,9 @@ def require_device_or_admin(
100105
# Try as machine API token first (verify_admin_jwt returns None on non-JWT input)
101106
for m in db.query(Machine).filter(Machine.active.is_(True)).all():
102107
if verify_api_token(token, m.api_token_hash):
108+
m.last_active_at = datetime.now(UTC).replace(tzinfo=None)
109+
db.commit()
110+
db.refresh(m)
103111
return m
104112
# Try admin session (cookie or Bearer JWT)
105113
user = get_session_user(request)

app/models/machine.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class Machine(Base):
2626
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=lambda: datetime.now(UTC).replace(tzinfo=None))
2727
created_by: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
2828
active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
29+
last_active_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
2930

3031
# Relationships
3132
admin_users: Mapped[list["MachineAdmin"]] = relationship(

app/schemas/machine.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class MachineResponse(BaseModel):
2626
active: bool
2727
created_at: datetime
2828
created_by: Optional[str]
29+
last_active_at: Optional[datetime] = None
2930

3031
model_config = ConfigDict(from_attributes=True)
3132

app/web/locales/de.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@
123123
"machines.btn_register_token": "Registrieren & Token erhalten",
124124
"machines.placeholder_name": "z.B. Lasercutter",
125125
"machines.placeholder_slug": "z.B. lasercutter",
126+
"machines.col_last_active": "Letzte Aktivität",
126127
"machines.col_actions": "Aktionen",
127128
"machines.btn_regen_token": "Token erneuern",
128129
"machines.regen_title": "Neuer Token für \"{name}\" — jetzt speichern!",

app/web/locales/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@
123123
"machines.btn_register_token": "Register & Get Token",
124124
"machines.placeholder_name": "e.g. Laser Cutter",
125125
"machines.placeholder_slug": "e.g. laser-cutter",
126+
"machines.col_last_active": "Last Active",
126127
"machines.col_actions": "Actions",
127128
"machines.btn_regen_token": "Regen Token",
128129
"machines.regen_title": "New token for \"{name}\" — save it now!",

app/web/templates/machines/detail.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ <h1 class="text-2xl font-bold" x-text="machine.name"></h1>
3232
<dd x-text="machine.machine_type"></dd>
3333
<dt class="text-gray-500">{{ _('common.created') }}</dt>
3434
<dd x-text="fmtDate(machine.created_at)"></dd>
35+
<dt class="text-gray-500">{{ _('machines.col_last_active') }}</dt>
36+
<dd x-text="machine.last_active_at ? fmtDateTime(machine.last_active_at) : '—'"></dd>
3537
</dl>
3638

3739
<!-- Admin-only edit form -->

app/web/templates/machines/list.html

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,16 @@ <h1 class="text-2xl font-bold">{{ _('machines.title') }}</h1>
4949
<th class="px-4 py-3 text-left">{{ _('machines.col_type') }}</th>
5050
<th class="px-4 py-3 text-center">{{ _('common.status') }}</th>
5151
<th class="px-4 py-3 text-left">{{ _('common.created') }}</th>
52+
<th class="px-4 py-3 text-left">{{ _('machines.col_last_active') }}</th>
5253
<th class="px-4 py-3 text-right">{{ _('machines.col_actions') }}</th>
5354
</tr>
5455
</thead>
5556
<tbody>
5657
<template x-if="loading">
57-
<tr><td colspan="6" class="px-4 py-8 text-center text-gray-400">Loading…</td></tr>
58+
<tr><td colspan="7" class="px-4 py-8 text-center text-gray-400">Loading…</td></tr>
5859
</template>
5960
<template x-if="!loading && machines.length === 0">
60-
<tr><td colspan="6" class="px-4 py-8 text-center text-gray-400">
61+
<tr><td colspan="7" class="px-4 py-8 text-center text-gray-400">
6162
<span x-show="isAdmin">{{ _('machines.empty') }}</span>
6263
<span x-show="!isAdmin">{{ _('machines.empty_managed') }}</span>
6364
</td></tr>
@@ -76,6 +77,8 @@ <h1 class="text-2xl font-bold">{{ _('machines.title') }}</h1>
7677
class="text-xs bg-red-100 text-red-600 rounded px-2 py-0.5">{{ _('common.inactive') }}</span>
7778
</td>
7879
<td class="px-4 py-2 text-gray-500 text-xs" x-text="fmtDate(m.created_at)"></td>
80+
<td class="px-4 py-2 text-gray-500 text-xs"
81+
x-text="m.last_active_at ? fmtDateTime(m.last_active_at) : '—'"></td>
7982
<td class="px-4 py-2 text-right whitespace-nowrap" x-show="isAdmin">
8083
<button @click="regenToken(m)"
8184
class="text-xs text-blue-600 hover:underline mr-3">{{ _('machines.btn_regen_token') }}</button>

0 commit comments

Comments
 (0)