feat(ui): add identity management for tenants and users
CI / build-and-test (push) Successful in 1m47s

* Implement tenant and user creation in IdentityPage
* Add API calls for managing tenants and users
* Introduce tenant-scoped API requests
* Update sidebar to include identity navigation
* Create BooleanStatusBadge component for key status
This commit is contained in:
2026-07-20 22:50:55 +02:00
parent 3d4e6d0939
commit 196b543bee
64 changed files with 3363 additions and 615 deletions
+17 -5
View File
@@ -1,7 +1,8 @@
Table agent_personas {
id bigserial [pk]
guid uuid [unique, not null, default: `gen_random_uuid()`]
name text [unique, not null]
name text [not null]
tenant_id text [ref: > tenants.id]
description text [not null, default: '']
summary text [not null]
detail text [not null, default: '']
@@ -11,12 +12,15 @@ Table agent_personas {
tags "text[]" [not null, default: `'{}'`]
created_at timestamptz [not null, default: `now()`]
updated_at timestamptz [not null, default: `now()`]
indexes { (tenant_id, name) [unique] tenant_id }
}
Table agent_parts {
id bigserial [pk]
guid uuid [unique, not null, default: `gen_random_uuid()`]
name text [unique, not null]
name text [not null]
tenant_id text [ref: > tenants.id]
part_type text [not null]
description text [not null, default: '']
summary text [not null]
@@ -24,6 +28,8 @@ Table agent_parts {
tags "text[]" [not null, default: `'{}'`]
created_at timestamptz [not null, default: `now()`]
updated_at timestamptz [not null, default: `now()`]
indexes { (tenant_id, name) [unique] tenant_id }
}
Table agent_persona_parts {
@@ -77,13 +83,16 @@ Table agent_persona_guardrails {
Table agent_traits {
id bigserial [pk]
guid uuid [unique, not null, default: `gen_random_uuid()`]
name text [unique, not null]
name text [not null]
tenant_id text [ref: > tenants.id]
trait_type text [not null]
description text [not null, default: '']
instruction text [not null, default: '']
tags "text[]" [not null, default: `'{}'`]
created_at timestamptz [not null, default: `now()`]
updated_at timestamptz [not null, default: `now()`]
indexes { (tenant_id, name) [unique] tenant_id }
}
Table agent_persona_traits {
@@ -98,11 +107,14 @@ Table agent_persona_traits {
Table character_arcs {
id bigserial [pk]
name text [unique, not null]
name text [not null]
tenant_id text [ref: > tenants.id]
description text [not null, default: '']
summary text [not null, default: '']
created_at timestamptz [not null, default: `now()`]
updated_at timestamptz [not null, default: `now()`]
indexes { (tenant_id, name) [unique] tenant_id }
}
Table arc_stages {
@@ -127,7 +139,7 @@ Table arc_stage_parts {
Table persona_arc {
id bigserial [pk]
persona_id bigint [pk, ref: > agent_personas.id]
persona_id bigint [unique, not null, ref: > agent_personas.id]
arc_id bigint [not null, ref: > character_arcs.id]
current_stage_id bigint [not null, ref: > arc_stages.id]
updated_at timestamptz [not null, default: `now()`]
+6 -6
View File
@@ -6,12 +6,12 @@ Table thoughts {
created_at timestamptz [default: `now()`]
updated_at timestamptz [default: `now()`]
project_id bigint [ref: > projects.id]
tenant_key text
tenant_id text [ref: > tenants.id]
archived_at timestamptz
indexes {
tenant_key
(tenant_key, project_id)
tenant_id
(tenant_id, project_id)
}
}
@@ -20,13 +20,13 @@ Table projects {
guid uuid [unique, not null, default: `gen_random_uuid()`]
name text [not null]
description text
tenant_key text
tenant_id text [ref: > tenants.id]
created_at timestamptz [default: `now()`]
last_active_at timestamptz [default: `now()`]
indexes {
(tenant_key, name) [unique]
tenant_key
(tenant_id, name) [unique]
tenant_id
}
}
+2 -2
View File
@@ -3,7 +3,7 @@ Table stored_files {
guid uuid [unique, not null, default: `gen_random_uuid()`]
thought_id bigint [ref: > thoughts.id]
project_id bigint [ref: > projects.id]
tenant_key text
tenant_id text [ref: > tenants.id]
name text [not null]
media_type text [not null]
kind text [not null, default: 'file']
@@ -17,7 +17,7 @@ Table stored_files {
indexes {
thought_id
project_id
tenant_key
tenant_id
sha256
}
}
+33
View File
@@ -0,0 +1,33 @@
Table tenants {
id text [pk]
name text [not null, unique]
created_at timestamptz [not null, default: `now()`]
}
Table tenant_users {
id text [pk]
tenant_id text [not null, ref: > tenants.id]
name text [not null]
email text
created_at timestamptz [not null, default: `now()`]
Indexes {
tenant_id
(tenant_id, email) [unique]
}
}
Table api_key_assignments {
key_id text [pk]
tenant_id text [not null, ref: > tenants.id]
user_id text [ref: > tenant_users.id]
description text [not null, default: '']
source text [not null]
enabled boolean [not null, default: true]
created_at timestamptz [not null, default: `now()`]
}
Table managed_api_keys {
key_id text [pk, ref: > api_key_assignments.key_id]
secret_hash text [not null]
}
+4 -4
View File
@@ -6,7 +6,7 @@ Table chat_histories {
channel text
agent_id text
project_id bigint [ref: > projects.id]
tenant_key text
tenant_id text [ref: > tenants.id]
messages jsonb [not null, default: `'[]'`]
summary text
metadata jsonb [not null, default: `'{}'`]
@@ -16,7 +16,7 @@ Table chat_histories {
indexes {
session_id
project_id
tenant_key
tenant_id
channel
agent_id
created_at
@@ -48,7 +48,7 @@ Table learnings {
source_type text
source_ref text
project_id bigint [ref: > projects.id]
tenant_key text
tenant_id text [ref: > tenants.id]
related_thought_id bigint [ref: > thoughts.id]
related_skill_id bigint [ref: > agent_skills.id]
reviewed_by text
@@ -61,7 +61,7 @@ Table learnings {
indexes {
project_id
tenant_key
tenant_id
category
area
status
+2 -2
View File
@@ -6,7 +6,7 @@ Table plans {
status text [not null, default: 'draft'] // draft, active, blocked, completed, cancelled, superseded
priority text [not null, default: 'medium'] // low, medium, high, critical
project_id bigint [ref: > projects.id]
tenant_key text
tenant_id text [ref: > tenants.id]
owner text
due_date timestamptz
completed_at timestamptz
@@ -19,7 +19,7 @@ Table plans {
indexes {
project_id
tenant_key
tenant_id
status
priority
owner
+8 -2
View File
@@ -1,7 +1,8 @@
Table agent_skills {
id bigserial [pk]
guid uuid [unique, not null, default: `gen_random_uuid()`]
name text [unique, not null]
name text [not null]
tenant_id text [ref: > tenants.id]
description text [not null, default: '']
content text [not null]
tags "text[]" [not null, default: `'{}'`]
@@ -11,18 +12,23 @@ Table agent_skills {
domain_tags "text[]" [not null, default: `'{}'`]
created_at timestamptz [not null, default: `now()`]
updated_at timestamptz [not null, default: `now()`]
indexes { (tenant_id, name) [unique] tenant_id }
}
Table agent_guardrails {
id bigserial [pk]
guid uuid [unique, not null, default: `gen_random_uuid()`]
name text [unique, not null]
name text [not null]
tenant_id text [ref: > tenants.id]
description text [not null, default: '']
content text [not null]
severity text [not null, default: 'medium']
tags "text[]" [not null, default: `'{}'`]
created_at timestamptz [not null, default: `now()`]
updated_at timestamptz [not null, default: `now()`]
indexes { (tenant_id, name) [unique] tenant_id }
}
Table project_skills {