feat(ui): add identity management for tenants and users
CI / build-and-test (push) Successful in 1m47s
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:
+1346
-109
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,31 @@
|
||||
create table if not exists tenants (
|
||||
id text primary key,
|
||||
name text not null unique,
|
||||
created_at timestamptz not null default now()
|
||||
);
|
||||
|
||||
create table if not exists tenant_users (
|
||||
id text primary key,
|
||||
tenant_id text not null references tenants(id),
|
||||
name text not null,
|
||||
email text,
|
||||
created_at timestamptz not null default now(),
|
||||
unique (tenant_id, email)
|
||||
);
|
||||
|
||||
create index if not exists tenant_users_tenant_id_idx on tenant_users (tenant_id);
|
||||
|
||||
create table if not exists api_key_assignments (
|
||||
key_id text primary key,
|
||||
tenant_id text not null references tenants(id),
|
||||
user_id text references tenant_users(id),
|
||||
description text not null default '',
|
||||
source text not null check (source in ('configured', 'managed')),
|
||||
enabled boolean not null default true,
|
||||
created_at timestamptz not null default now()
|
||||
);
|
||||
|
||||
create table if not exists managed_api_keys (
|
||||
key_id text primary key references api_key_assignments(key_id) on delete cascade,
|
||||
secret_hash text not null
|
||||
);
|
||||
@@ -0,0 +1,53 @@
|
||||
-- Convert tenant_key from an opaque authentication value into a real tenant
|
||||
-- relationship. Preserve legacy rows and create placeholders for historical
|
||||
-- key IDs before adding foreign keys.
|
||||
insert into tenants (id, name)
|
||||
select tenant_key, tenant_key
|
||||
from (
|
||||
select tenant_key from projects
|
||||
union select tenant_key from thoughts
|
||||
union select tenant_key from stored_files
|
||||
union select tenant_key from learnings
|
||||
union select tenant_key from plans
|
||||
union select tenant_key from chat_histories
|
||||
) tenant_keys
|
||||
where tenant_key is not null and tenant_key <> ''
|
||||
on conflict (id) do nothing;
|
||||
|
||||
alter table agent_skills add column if not exists tenant_key text references tenants(id);
|
||||
alter table agent_guardrails add column if not exists tenant_key text references tenants(id);
|
||||
alter table agent_personas add column if not exists tenant_key text references tenants(id);
|
||||
alter table agent_parts add column if not exists tenant_key text references tenants(id);
|
||||
alter table agent_traits add column if not exists tenant_key text references tenants(id);
|
||||
alter table character_arcs add column if not exists tenant_key text references tenants(id);
|
||||
|
||||
alter table agent_skills drop constraint if exists ukey_agent_skills_name;
|
||||
alter table agent_guardrails drop constraint if exists ukey_agent_guardrails_name;
|
||||
alter table agent_personas drop constraint if exists ukey_agent_personas_name;
|
||||
alter table agent_parts drop constraint if exists ukey_agent_parts_name;
|
||||
alter table agent_traits drop constraint if exists ukey_agent_traits_name;
|
||||
alter table character_arcs drop constraint if exists ukey_character_arcs_name;
|
||||
|
||||
create unique index if not exists agent_skills_tenant_key_name_idx on agent_skills (coalesce(tenant_key, ''), name);
|
||||
create unique index if not exists agent_guardrails_tenant_key_name_idx on agent_guardrails (coalesce(tenant_key, ''), name);
|
||||
create unique index if not exists agent_personas_tenant_key_name_idx on agent_personas (coalesce(tenant_key, ''), name);
|
||||
create unique index if not exists agent_parts_tenant_key_name_idx on agent_parts (coalesce(tenant_key, ''), name);
|
||||
create unique index if not exists agent_traits_tenant_key_name_idx on agent_traits (coalesce(tenant_key, ''), name);
|
||||
create unique index if not exists character_arcs_tenant_key_name_idx on character_arcs (coalesce(tenant_key, ''), name);
|
||||
|
||||
create index if not exists agent_skills_tenant_key_idx on agent_skills (tenant_key);
|
||||
create index if not exists agent_guardrails_tenant_key_idx on agent_guardrails (tenant_key);
|
||||
create index if not exists agent_personas_tenant_key_idx on agent_personas (tenant_key);
|
||||
create index if not exists agent_parts_tenant_key_idx on agent_parts (tenant_key);
|
||||
create index if not exists agent_traits_tenant_key_idx on agent_traits (tenant_key);
|
||||
create index if not exists character_arcs_tenant_key_idx on character_arcs (tenant_key);
|
||||
|
||||
do $$
|
||||
declare table_name text;
|
||||
begin
|
||||
foreach table_name in array array['projects', 'thoughts', 'stored_files', 'learnings', 'plans', 'chat_histories']
|
||||
loop
|
||||
execute format('alter table %I drop constraint if exists %I', table_name, table_name || '_tenant_key_fkey');
|
||||
execute format('alter table %I add constraint %I foreign key (tenant_key) references tenants(id)', table_name, table_name || '_tenant_key_fkey');
|
||||
end loop;
|
||||
end $$;
|
||||
@@ -0,0 +1,44 @@
|
||||
-- Tenant data is not in use yet, so replace the earlier opaque tenant_key
|
||||
-- column name with the relational tenant_id convention everywhere.
|
||||
do $$
|
||||
declare
|
||||
tbl text;
|
||||
con text;
|
||||
begin
|
||||
foreach tbl in array array[
|
||||
'projects', 'thoughts', 'stored_files', 'learnings', 'plans', 'chat_histories',
|
||||
'agent_skills', 'agent_guardrails', 'agent_personas', 'agent_parts',
|
||||
'agent_traits', 'character_arcs'
|
||||
] loop
|
||||
-- Fresh installs already have tenant_id from the regenerated schema;
|
||||
-- discard that empty column so the historical migration chain converges.
|
||||
if exists (
|
||||
select 1 from information_schema.columns
|
||||
where table_schema = 'public' and table_name = tbl and column_name = 'tenant_id'
|
||||
) and exists (
|
||||
select 1 from information_schema.columns
|
||||
where table_schema = 'public' and table_name = tbl and column_name = 'tenant_key'
|
||||
) then
|
||||
execute format('alter table %I drop column tenant_id', tbl);
|
||||
end if;
|
||||
if exists (
|
||||
select 1 from information_schema.columns
|
||||
where table_schema = 'public' and table_name = tbl and column_name = 'tenant_key'
|
||||
) then
|
||||
execute format('alter table %I rename column tenant_key to tenant_id', tbl);
|
||||
end if;
|
||||
|
||||
for con in
|
||||
select c.conname
|
||||
from pg_constraint c
|
||||
join pg_class r on r.oid = c.conrelid
|
||||
join pg_namespace n on n.oid = r.relnamespace
|
||||
join unnest(c.conkey) as k(attnum) on true
|
||||
join pg_attribute a on a.attrelid = r.oid and a.attnum = k.attnum
|
||||
where n.nspname = 'public' and r.relname = tbl and c.contype = 'f' and a.attname = 'tenant_id'
|
||||
loop
|
||||
execute format('alter table %I drop constraint %I', tbl, con);
|
||||
end loop;
|
||||
execute format('alter table %I add constraint %I foreign key (tenant_id) references tenants(id)', tbl, 'fk_' || tbl || '_tenant_id');
|
||||
end loop;
|
||||
end $$;
|
||||
Reference in New Issue
Block a user