diff --git a/client-v3/src/components/config/ConfigUsers.vue b/client-v3/src/components/config/ConfigUsers.vue
index 62b3fa83..0b1b10c9 100644
--- a/client-v3/src/components/config/ConfigUsers.vue
+++ b/client-v3/src/components/config/ConfigUsers.vue
@@ -10,6 +10,9 @@
User Type
+
+ {{ data.item.created_on ?? 'N/A' }}
+
{{ data.item.last_login ?? 'Never' }}
@@ -105,6 +108,7 @@ const selectedUser = ref<{ id: number; username: string } | null>(null);
const userFields = [
'username',
+ 'created_on',
'last_login',
'last_seen',
{ key: 'is_admin', label: 'User Type' },
diff --git a/client-v3/src/types/api/user.ts b/client-v3/src/types/api/user.ts
index e37f8f3b..bdb8d311 100644
--- a/client-v3/src/types/api/user.ts
+++ b/client-v3/src/types/api/user.ts
@@ -2,6 +2,7 @@ export interface User {
id: number;
username: string | null;
is_admin: boolean | null;
+ created_on: string | null;
last_login: string | null;
last_seen: string | null;
requires_password_change: boolean;
diff --git a/client/src/types/api/user.ts b/client/src/types/api/user.ts
index 98e204a2..d0ee6261 100644
--- a/client/src/types/api/user.ts
+++ b/client/src/types/api/user.ts
@@ -2,6 +2,7 @@ export interface User {
id: number;
username: string | null;
is_admin: boolean | null;
+ created_on: string | null;
last_login: string | null;
last_seen: string | null;
requires_password_change: boolean;
diff --git a/client/src/vue_components/config/ConfigUsers.vue b/client/src/vue_components/config/ConfigUsers.vue
index c045c580..3edb0e1a 100644
--- a/client/src/vue_components/config/ConfigUsers.vue
+++ b/client/src/vue_components/config/ConfigUsers.vue
@@ -10,6 +10,9 @@
User Type
+
+ {{ data.item.created_on ? data.item.created_on : 'N/A' }}
+
{{ data.item.last_login ? data.item.last_login : 'Never' }}
@@ -93,7 +96,14 @@ export default defineComponent({
components: { CreateUser, ConfigRbac, ResetPassword },
data() {
return {
- userFields: ['username', 'last_login', 'last_seen', 'is_admin', { key: 'btn', label: '' }],
+ userFields: [
+ 'username',
+ 'created_on',
+ 'last_login',
+ 'last_seen',
+ 'is_admin',
+ { key: 'btn', label: '' },
+ ],
editUser: null as number | null,
resetUser: null as { id: number; username: string } | null,
clientTimeout: null as ReturnType | null,
diff --git a/server/alembic_config/versions/3f2343a8a936_add_created_on_timestamp_tp_users.py b/server/alembic_config/versions/3f2343a8a936_add_created_on_timestamp_tp_users.py
new file mode 100644
index 00000000..34a4ca80
--- /dev/null
+++ b/server/alembic_config/versions/3f2343a8a936_add_created_on_timestamp_tp_users.py
@@ -0,0 +1,35 @@
+"""Add created on timestamp tp users
+
+Revision ID: 3f2343a8a936
+Revises: 11311df29aa4
+Create Date: 2026-06-06 23:49:09.107684
+
+"""
+
+from typing import Sequence, Union
+
+import sqlalchemy as sa
+from alembic import op
+
+
+# revision identifiers, used by Alembic.
+revision: str = "3f2343a8a936"
+down_revision: Union[str, None] = "11311df29aa4"
+branch_labels: Union[str, Sequence[str], None] = None
+depends_on: Union[str, Sequence[str], None] = None
+
+
+def upgrade() -> None:
+ # ### commands auto generated by Alembic - please adjust! ###
+ with op.batch_alter_table("user", schema=None) as batch_op:
+ batch_op.add_column(sa.Column("created_on", sa.DateTime(), nullable=True))
+
+ # ### end Alembic commands ###
+
+
+def downgrade() -> None:
+ # ### commands auto generated by Alembic - please adjust! ###
+ with op.batch_alter_table("user", schema=None) as batch_op:
+ batch_op.drop_column("created_on")
+
+ # ### end Alembic commands ###
diff --git a/server/controllers/api/auth/user.py b/server/controllers/api/auth/user.py
index d7248d23..4e3be9bf 100644
--- a/server/controllers/api/auth/user.py
+++ b/server/controllers/api/auth/user.py
@@ -72,6 +72,7 @@ async def post(self):
username=username,
password=hashed_password,
is_admin=is_admin,
+ created_on=datetime.now(tz=timezone.utc),
)
)
session.commit()
diff --git a/server/models/user.py b/server/models/user.py
index 089080ac..4371dbb4 100644
--- a/server/models/user.py
+++ b/server/models/user.py
@@ -67,6 +67,7 @@ class User(db.Model):
username: Mapped[str | None] = mapped_column(index=True)
password: Mapped[str | None] = mapped_column()
is_admin: Mapped[bool | None] = mapped_column()
+ created_on: Mapped[datetime.datetime | None] = mapped_column()
last_login: Mapped[datetime.datetime | None] = mapped_column()
last_seen: Mapped[datetime.datetime | None] = mapped_column()
api_token: Mapped[str | None] = mapped_column(index=True)