KiWA001 commited on
Commit
ba9d716
·
1 Parent(s): 78e29c5

fix: use lowercase table names for PostgreSQL compatibility

Browse files

PostgreSQL converts unquoted identifiers to lowercase, causing table not found errors.
Changed all KAIAPI_ prefixes to kaiapi_ (lowercase) to match PostgreSQL behavior.

Tables now:
- kaiapi_api_keys
- kaiapi_model_stats
- kaiapi_provider_sessions
- kaiapi_provider_states

This fixes the 'Could not find the table' error when listing API keys.

Files changed (7) hide show
  1. admin_router.py +5 -5
  2. auth.py +1 -1
  3. engine.py +4 -4
  4. provider_sessions.py +4 -4
  5. provider_state.py +4 -4
  6. supabase_setup.sql +50 -50
  7. v1_router.py +2 -2
admin_router.py CHANGED
@@ -43,7 +43,7 @@ async def list_keys():
43
  raise HTTPException(status_code=503, detail="Database unavailable")
44
 
45
  try:
46
- res = supabase.table("KAIAPI_api_keys").select("*").order("created_at", desc=True).execute()
47
  return res.data
48
  except Exception as e:
49
  raise HTTPException(status_code=500, detail=str(e))
@@ -67,7 +67,7 @@ async def create_key(req: CreateKeyRequest):
67
  }
68
 
69
  try:
70
- res = supabase.table("KAIAPI_api_keys").insert(new_key).execute()
71
  if res.data:
72
  return res.data[0]
73
  raise HTTPException(status_code=500, detail="Failed to create key")
@@ -84,7 +84,7 @@ async def revoke_key(key_id: str):
84
  try:
85
  # Check if exists first? Or just delete.
86
  # Hard delete for now, or soft delete if we had is_active column logic in router update, but delete is cleaner for management
87
- res = supabase.table("KAIAPI_api_keys").delete().eq("id", key_id).execute()
88
  return {"status": "success", "deleted": key_id}
89
  except Exception as e:
90
  raise HTTPException(status_code=500, detail=str(e))
@@ -97,7 +97,7 @@ async def reset_usage(key_id: str):
97
  raise HTTPException(status_code=503, detail="Database unavailable")
98
 
99
  try:
100
- supabase.table("KAIAPI_api_keys").update({"usage_tokens": 0}).eq("id", key_id).execute()
101
  return {"status": "reset"}
102
  except Exception as e:
103
  raise HTTPException(status_code=500, detail=str(e))
@@ -113,7 +113,7 @@ async def lookup_key_by_token(req: LookupKeyRequest):
113
  raise HTTPException(status_code=400, detail="Invalid token format")
114
 
115
  try:
116
- res = supabase.table("KAIAPI_api_keys").select("*").eq("token", req.token).execute()
117
 
118
  if not res.data or len(res.data) == 0:
119
  raise HTTPException(status_code=404, detail="Key not found")
 
43
  raise HTTPException(status_code=503, detail="Database unavailable")
44
 
45
  try:
46
+ res = supabase.table("kaiapi_api_keys").select("*").order("created_at", desc=True).execute()
47
  return res.data
48
  except Exception as e:
49
  raise HTTPException(status_code=500, detail=str(e))
 
67
  }
68
 
69
  try:
70
+ res = supabase.table("kaiapi_api_keys").insert(new_key).execute()
71
  if res.data:
72
  return res.data[0]
73
  raise HTTPException(status_code=500, detail="Failed to create key")
 
84
  try:
85
  # Check if exists first? Or just delete.
86
  # Hard delete for now, or soft delete if we had is_active column logic in router update, but delete is cleaner for management
87
+ res = supabase.table("kaiapi_api_keys").delete().eq("id", key_id).execute()
88
  return {"status": "success", "deleted": key_id}
89
  except Exception as e:
90
  raise HTTPException(status_code=500, detail=str(e))
 
97
  raise HTTPException(status_code=503, detail="Database unavailable")
98
 
99
  try:
100
+ supabase.table("kaiapi_api_keys").update({"usage_tokens": 0}).eq("id", key_id).execute()
101
  return {"status": "reset"}
102
  except Exception as e:
103
  raise HTTPException(status_code=500, detail=str(e))
 
113
  raise HTTPException(status_code=400, detail="Invalid token format")
114
 
115
  try:
116
+ res = supabase.table("kaiapi_api_keys").select("*").eq("token", req.token).execute()
117
 
118
  if not res.data or len(res.data) == 0:
119
  raise HTTPException(status_code=404, detail="Key not found")
auth.py CHANGED
@@ -84,7 +84,7 @@ async def verify_api_key(
84
  if not supabase:
85
  raise HTTPException(status_code=503, detail="Service unavailable")
86
 
87
- res = supabase.table("KAIAPI_api_keys").select("*").eq("token", token).execute()
88
  if not res.data:
89
  return None
90
  return res.data[0]
 
84
  if not supabase:
85
  raise HTTPException(status_code=503, detail="Service unavailable")
86
 
87
+ res = supabase.table("kaiapi_api_keys").select("*").eq("token", token).execute()
88
  if not res.data:
89
  return None
90
  return res.data[0]
engine.py CHANGED
@@ -102,7 +102,7 @@ class AIEngine:
102
 
103
  try:
104
  # Fetch all stats
105
- response = self.supabase.table("KAIAPI_model_stats").select("*").execute()
106
  for row in response.data:
107
  self._stats[row['id']] = {
108
  "success": row.get('success', 0),
@@ -132,7 +132,7 @@ class AIEngine:
132
  "total_time_ms": data.get("total_time_ms", 0),
133
  "count_samples": data.get("count_samples", 0)
134
  }
135
- self.supabase.table("KAIAPI_model_stats").upsert(record).execute()
136
  except Exception as e:
137
  logger.error(f"Failed to save stats for {key}: {e}")
138
 
@@ -294,7 +294,7 @@ class AIEngine:
294
  if self.supabase:
295
  try:
296
  # Delete all rows
297
- self.supabase.table("KAIAPI_model_stats").delete().neq("id", "0").execute()
298
  logger.info("Cleared all stats from Supabase")
299
  except Exception as e:
300
  logger.error(f"Failed to clear Supabase stats: {e}")
@@ -623,7 +623,7 @@ class AIEngine:
623
 
624
  # Attempt to reset Supabase (blocking, but necessary for persistence)
625
  if self.supabase:
626
- self.supabase.table("KAIAPI_model_stats").update({"consecutive_failures": 0}).gt("consecutive_failures", 0).execute()
627
  except Exception as reset_err:
628
  logger.error(f"Failed to auto-reset stats: {reset_err}")
629
 
 
102
 
103
  try:
104
  # Fetch all stats
105
+ response = self.supabase.table("kaiapi_model_stats").select("*").execute()
106
  for row in response.data:
107
  self._stats[row['id']] = {
108
  "success": row.get('success', 0),
 
132
  "total_time_ms": data.get("total_time_ms", 0),
133
  "count_samples": data.get("count_samples", 0)
134
  }
135
+ self.supabase.table("kaiapi_model_stats").upsert(record).execute()
136
  except Exception as e:
137
  logger.error(f"Failed to save stats for {key}: {e}")
138
 
 
294
  if self.supabase:
295
  try:
296
  # Delete all rows
297
+ self.supabase.table("kaiapi_model_stats").delete().neq("id", "0").execute()
298
  logger.info("Cleared all stats from Supabase")
299
  except Exception as e:
300
  logger.error(f"Failed to clear Supabase stats: {e}")
 
623
 
624
  # Attempt to reset Supabase (blocking, but necessary for persistence)
625
  if self.supabase:
626
+ self.supabase.table("kaiapi_model_stats").update({"consecutive_failures": 0}).gt("consecutive_failures", 0).execute()
627
  except Exception as reset_err:
628
  logger.error(f"Failed to auto-reset stats: {reset_err}")
629
 
provider_sessions.py CHANGED
@@ -62,7 +62,7 @@ class ProviderSessionManager:
62
  return None
63
 
64
  try:
65
- response = self.supabase.table("KAIAPI_provider_sessions").select("*").eq("provider", provider).execute()
66
 
67
  if not response.data:
68
  return None
@@ -166,7 +166,7 @@ class ProviderSessionManager:
166
  return False
167
 
168
  try:
169
- self.supabase.table("KAIAPI_provider_sessions").delete().eq("provider", provider).execute()
170
  logger.info(f"Deleted session for {provider}")
171
  return True
172
  except Exception as e:
@@ -181,7 +181,7 @@ class ProviderSessionManager:
181
  return False
182
 
183
  try:
184
- self.supabase.table("KAIAPI_provider_sessions").delete().neq("id", "00000000-0000-0000-0000-000000000000").execute()
185
  logger.info("Cleared all provider sessions")
186
  return True
187
  except Exception as e:
@@ -196,7 +196,7 @@ class ProviderSessionManager:
196
  return []
197
 
198
  try:
199
- response = self.supabase.table("KAIAPI_provider_sessions").select("*").execute()
200
  return response.data
201
  except Exception as e:
202
  logger.error(f"Failed to get all sessions: {e}")
 
62
  return None
63
 
64
  try:
65
+ response = self.supabase.table("kaiapi_provider_sessions").select("*").eq("provider", provider).execute()
66
 
67
  if not response.data:
68
  return None
 
166
  return False
167
 
168
  try:
169
+ self.supabase.table("kaiapi_provider_sessions").delete().eq("provider", provider).execute()
170
  logger.info(f"Deleted session for {provider}")
171
  return True
172
  except Exception as e:
 
181
  return False
182
 
183
  try:
184
+ self.supabase.table("kaiapi_provider_sessions").delete().neq("id", "00000000-0000-0000-0000-000000000000").execute()
185
  logger.info("Cleared all provider sessions")
186
  return True
187
  except Exception as e:
 
196
  return []
197
 
198
  try:
199
+ response = self.supabase.table("kaiapi_provider_sessions").select("*").execute()
200
  return response.data
201
  except Exception as e:
202
  logger.error(f"Failed to get all sessions: {e}")
provider_state.py CHANGED
@@ -2,7 +2,7 @@
2
  Provider State Manager
3
  ----------------------
4
  Manages enabled/disabled state of providers with Supabase persistence.
5
- Uses KAIAPI_ prefixed table names for multi-project organization.
6
  """
7
 
8
  import logging
@@ -12,8 +12,8 @@ from config import PROVIDERS
12
 
13
  logger = logging.getLogger("kai_api.provider_state")
14
 
15
- # Table name with KAIAPI_ prefix
16
- TABLE_NAME = "KAIAPI_provider_states"
17
 
18
  class ProviderStateManager:
19
  """Manages provider enable/disable state with Supabase persistence."""
@@ -31,7 +31,7 @@ class ProviderStateManager:
31
 
32
  if supabase:
33
  try:
34
- # Try to load from Supabase (using KAIAPI_ prefixed table)
35
  res = supabase.table(TABLE_NAME).select("*").execute()
36
 
37
  if res.data:
 
2
  Provider State Manager
3
  ----------------------
4
  Manages enabled/disabled state of providers with Supabase persistence.
5
+ Uses kaiapi_ prefixed table names for multi-project organization.
6
  """
7
 
8
  import logging
 
12
 
13
  logger = logging.getLogger("kai_api.provider_state")
14
 
15
+ # Table name with kaiapi_ prefix
16
+ TABLE_NAME = "kaiapi_provider_states"
17
 
18
  class ProviderStateManager:
19
  """Manages provider enable/disable state with Supabase persistence."""
 
31
 
32
  if supabase:
33
  try:
34
+ # Try to load from Supabase (using kaiapi_ prefixed table)
35
  res = supabase.table(TABLE_NAME).select("*").execute()
36
 
37
  if res.data:
supabase_setup.sql CHANGED
@@ -1,11 +1,11 @@
1
  -- ============================================
2
  -- K-AI API Gateway - Complete Supabase SQL Setup
3
  -- ============================================
4
- -- This script handles both creating tables with KAIAPI_ prefix
5
  -- and migrating data from old tables (if they exist)
6
 
7
  -- ============================================
8
- -- STEP 1: Rename existing tables to add KAIAPI_ prefix
9
  -- ============================================
10
 
11
  -- Rename api_keys table if it exists
@@ -14,8 +14,8 @@ BEGIN
14
  IF EXISTS (SELECT FROM information_schema.tables
15
  WHERE table_schema = 'public'
16
  AND table_name = 'api_keys') THEN
17
- ALTER TABLE api_keys RENAME TO KAIAPI_api_keys;
18
- RAISE NOTICE 'Renamed api_keys to KAIAPI_api_keys';
19
  END IF;
20
  END $$;
21
 
@@ -25,8 +25,8 @@ BEGIN
25
  IF EXISTS (SELECT FROM information_schema.tables
26
  WHERE table_schema = 'public'
27
  AND table_name = 'model_stats') THEN
28
- ALTER TABLE model_stats RENAME TO KAIAPI_model_stats;
29
- RAISE NOTICE 'Renamed model_stats to KAIAPI_model_stats';
30
  END IF;
31
  END $$;
32
 
@@ -36,8 +36,8 @@ BEGIN
36
  IF EXISTS (SELECT FROM information_schema.tables
37
  WHERE table_schema = 'public'
38
  AND table_name = 'provider_sessions') THEN
39
- ALTER TABLE provider_sessions RENAME TO KAIAPI_provider_sessions;
40
- RAISE NOTICE 'Renamed provider_sessions to KAIAPI_provider_sessions';
41
  END IF;
42
  END $$;
43
 
@@ -47,15 +47,15 @@ BEGIN
47
  IF EXISTS (SELECT FROM information_schema.tables
48
  WHERE table_schema = 'public'
49
  AND table_name = 'provider_states') THEN
50
- ALTER TABLE provider_states RENAME TO KAIAPI_provider_states;
51
- RAISE NOTICE 'Renamed provider_states to KAIAPI_provider_states';
52
  END IF;
53
  END $$;
54
 
55
  -- ============================================
56
- -- STEP 2: Create KAIAPI_api_keys table (if not exists)
57
  -- ============================================
58
- CREATE TABLE IF NOT EXISTS KAIAPI_api_keys (
59
  id SERIAL PRIMARY KEY,
60
  name VARCHAR(255) NOT NULL,
61
  token VARCHAR(255) UNIQUE NOT NULL,
@@ -66,13 +66,13 @@ CREATE TABLE IF NOT EXISTS KAIAPI_api_keys (
66
  updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
67
  );
68
 
69
- CREATE INDEX IF NOT EXISTS idx_KAIAPI_api_keys_token ON KAIAPI_api_keys(token);
70
- CREATE INDEX IF NOT EXISTS idx_KAIAPI_api_keys_is_active ON KAIAPI_api_keys(is_active);
71
 
72
  -- ============================================
73
- -- STEP 3: Create KAIAPI_model_stats table (if not exists)
74
  -- ============================================
75
- CREATE TABLE IF NOT EXISTS KAIAPI_model_stats (
76
  id VARCHAR(255) PRIMARY KEY,
77
  success INTEGER NOT NULL DEFAULT 0,
78
  failure INTEGER NOT NULL DEFAULT 0,
@@ -84,12 +84,12 @@ CREATE TABLE IF NOT EXISTS KAIAPI_model_stats (
84
  updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
85
  );
86
 
87
- CREATE INDEX IF NOT EXISTS idx_KAIAPI_model_stats_id ON KAIAPI_model_stats(id);
88
 
89
  -- ============================================
90
- -- STEP 4: Create KAIAPI_provider_sessions table (if not exists)
91
  -- ============================================
92
- CREATE TABLE IF NOT EXISTS KAIAPI_provider_sessions (
93
  id SERIAL PRIMARY KEY,
94
  provider VARCHAR(50) UNIQUE NOT NULL,
95
  cookies JSONB,
@@ -98,12 +98,12 @@ CREATE TABLE IF NOT EXISTS KAIAPI_provider_sessions (
98
  updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
99
  );
100
 
101
- CREATE INDEX IF NOT EXISTS idx_KAIAPI_provider_sessions_provider ON KAIAPI_provider_sessions(provider);
102
 
103
  -- ============================================
104
- -- STEP 5: Create KAIAPI_provider_states table (NEW - for toggle management)
105
  -- ============================================
106
- CREATE TABLE IF NOT EXISTS KAIAPI_provider_states (
107
  id SERIAL PRIMARY KEY,
108
  provider_id VARCHAR(50) UNIQUE NOT NULL,
109
  name VARCHAR(100) NOT NULL,
@@ -113,11 +113,11 @@ CREATE TABLE IF NOT EXISTS KAIAPI_provider_states (
113
  updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
114
  );
115
 
116
- CREATE INDEX IF NOT EXISTS idx_KAIAPI_provider_states_provider_id ON KAIAPI_provider_states(provider_id);
117
- CREATE INDEX IF NOT EXISTS idx_KAIAPI_provider_states_enabled ON KAIAPI_provider_states(enabled);
118
 
119
  -- Insert default providers (if table is empty)
120
- INSERT INTO KAIAPI_provider_states (provider_id, name, type, enabled) VALUES
121
  ('g4f', 'G4F (Free GPT-4)', 'api', true),
122
  ('zai', 'Z.ai (GLM-5)', 'api', true),
123
  ('gemini', 'Google Gemini', 'api', true),
@@ -141,27 +141,27 @@ END;
141
  $$ language 'plpgsql';
142
 
143
  -- Create triggers for all tables
144
- DROP TRIGGER IF EXISTS update_KAIAPI_api_keys_updated_at ON KAIAPI_api_keys;
145
- CREATE TRIGGER update_KAIAPI_api_keys_updated_at
146
- BEFORE UPDATE ON KAIAPI_api_keys
147
  FOR EACH ROW
148
  EXECUTE FUNCTION update_updated_at_column();
149
 
150
- DROP TRIGGER IF EXISTS update_KAIAPI_model_stats_updated_at ON KAIAPI_model_stats;
151
- CREATE TRIGGER update_KAIAPI_model_stats_updated_at
152
- BEFORE UPDATE ON KAIAPI_model_stats
153
  FOR EACH ROW
154
  EXECUTE FUNCTION update_updated_at_column();
155
 
156
- DROP TRIGGER IF EXISTS update_KAIAPI_provider_sessions_updated_at ON KAIAPI_provider_sessions;
157
- CREATE TRIGGER update_KAIAPI_provider_sessions_updated_at
158
- BEFORE UPDATE ON KAIAPI_provider_sessions
159
  FOR EACH ROW
160
  EXECUTE FUNCTION update_updated_at_column();
161
 
162
- DROP TRIGGER IF EXISTS update_KAIAPI_provider_states_updated_at ON KAIAPI_provider_states;
163
- CREATE TRIGGER update_KAIAPI_provider_states_updated_at
164
- BEFORE UPDATE ON KAIAPI_provider_states
165
  FOR EACH ROW
166
  EXECUTE FUNCTION update_updated_at_column();
167
 
@@ -170,35 +170,35 @@ CREATE TRIGGER update_KAIAPI_provider_states_updated_at
170
  -- ============================================
171
  -- Uncomment the following lines if you want to enable RLS
172
 
173
- -- ALTER TABLE KAIAPI_api_keys ENABLE ROW LEVEL SECURITY;
174
- -- ALTER TABLE KAIAPI_model_stats ENABLE ROW LEVEL SECURITY;
175
- -- ALTER TABLE KAIAPI_provider_sessions ENABLE ROW LEVEL SECURITY;
176
- -- ALTER TABLE KAIAPI_provider_states ENABLE ROW LEVEL SECURITY;
177
 
178
  -- Create policy to allow all operations (adjust as needed)
179
- -- CREATE POLICY "Allow all operations on KAIAPI_api_keys"
180
- -- ON KAIAPI_api_keys
181
  -- FOR ALL
182
  -- TO anon, authenticated
183
  -- USING (true)
184
  -- WITH CHECK (true);
185
 
186
- -- CREATE POLICY "Allow all operations on KAIAPI_model_stats"
187
- -- ON KAIAPI_model_stats
188
  -- FOR ALL
189
  -- TO anon, authenticated
190
  -- USING (true)
191
  -- WITH CHECK (true);
192
 
193
- -- CREATE POLICY "Allow all operations on KAIAPI_provider_sessions"
194
- -- ON KAIAPI_provider_sessions
195
  -- FOR ALL
196
  -- TO anon, authenticated
197
  -- USING (true)
198
  -- WITH CHECK (true);
199
 
200
- -- CREATE POLICY "Allow all operations on KAIAPI_provider_states"
201
- -- ON KAIAPI_provider_states
202
  -- FOR ALL
203
  -- TO anon, authenticated
204
  -- USING (true)
@@ -211,5 +211,5 @@ SELECT 'Tables created successfully:' as message;
211
  SELECT table_name
212
  FROM information_schema.tables
213
  WHERE table_schema = 'public'
214
- AND table_name LIKE 'KAIAPI_%'
215
  ORDER BY table_name;
 
1
  -- ============================================
2
  -- K-AI API Gateway - Complete Supabase SQL Setup
3
  -- ============================================
4
+ -- This script handles both creating tables with kaiapi_ prefix
5
  -- and migrating data from old tables (if they exist)
6
 
7
  -- ============================================
8
+ -- STEP 1: Rename existing tables to add kaiapi_ prefix
9
  -- ============================================
10
 
11
  -- Rename api_keys table if it exists
 
14
  IF EXISTS (SELECT FROM information_schema.tables
15
  WHERE table_schema = 'public'
16
  AND table_name = 'api_keys') THEN
17
+ ALTER TABLE api_keys RENAME TO kaiapi_api_keys;
18
+ RAISE NOTICE 'Renamed api_keys to kaiapi_api_keys';
19
  END IF;
20
  END $$;
21
 
 
25
  IF EXISTS (SELECT FROM information_schema.tables
26
  WHERE table_schema = 'public'
27
  AND table_name = 'model_stats') THEN
28
+ ALTER TABLE model_stats RENAME TO kaiapi_model_stats;
29
+ RAISE NOTICE 'Renamed model_stats to kaiapi_model_stats';
30
  END IF;
31
  END $$;
32
 
 
36
  IF EXISTS (SELECT FROM information_schema.tables
37
  WHERE table_schema = 'public'
38
  AND table_name = 'provider_sessions') THEN
39
+ ALTER TABLE provider_sessions RENAME TO kaiapi_provider_sessions;
40
+ RAISE NOTICE 'Renamed provider_sessions to kaiapi_provider_sessions';
41
  END IF;
42
  END $$;
43
 
 
47
  IF EXISTS (SELECT FROM information_schema.tables
48
  WHERE table_schema = 'public'
49
  AND table_name = 'provider_states') THEN
50
+ ALTER TABLE provider_states RENAME TO kaiapi_provider_states;
51
+ RAISE NOTICE 'Renamed provider_states to kaiapi_provider_states';
52
  END IF;
53
  END $$;
54
 
55
  -- ============================================
56
+ -- STEP 2: Create kaiapi_api_keys table (if not exists)
57
  -- ============================================
58
+ CREATE TABLE IF NOT EXISTS kaiapi_api_keys (
59
  id SERIAL PRIMARY KEY,
60
  name VARCHAR(255) NOT NULL,
61
  token VARCHAR(255) UNIQUE NOT NULL,
 
66
  updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
67
  );
68
 
69
+ CREATE INDEX IF NOT EXISTS idx_kaiapi_api_keys_token ON kaiapi_api_keys(token);
70
+ CREATE INDEX IF NOT EXISTS idx_kaiapi_api_keys_is_active ON kaiapi_api_keys(is_active);
71
 
72
  -- ============================================
73
+ -- STEP 3: Create kaiapi_model_stats table (if not exists)
74
  -- ============================================
75
+ CREATE TABLE IF NOT EXISTS kaiapi_model_stats (
76
  id VARCHAR(255) PRIMARY KEY,
77
  success INTEGER NOT NULL DEFAULT 0,
78
  failure INTEGER NOT NULL DEFAULT 0,
 
84
  updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
85
  );
86
 
87
+ CREATE INDEX IF NOT EXISTS idx_kaiapi_model_stats_id ON kaiapi_model_stats(id);
88
 
89
  -- ============================================
90
+ -- STEP 4: Create kaiapi_provider_sessions table (if not exists)
91
  -- ============================================
92
+ CREATE TABLE IF NOT EXISTS kaiapi_provider_sessions (
93
  id SERIAL PRIMARY KEY,
94
  provider VARCHAR(50) UNIQUE NOT NULL,
95
  cookies JSONB,
 
98
  updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
99
  );
100
 
101
+ CREATE INDEX IF NOT EXISTS idx_kaiapi_provider_sessions_provider ON kaiapi_provider_sessions(provider);
102
 
103
  -- ============================================
104
+ -- STEP 5: Create kaiapi_provider_states table (NEW - for toggle management)
105
  -- ============================================
106
+ CREATE TABLE IF NOT EXISTS kaiapi_provider_states (
107
  id SERIAL PRIMARY KEY,
108
  provider_id VARCHAR(50) UNIQUE NOT NULL,
109
  name VARCHAR(100) NOT NULL,
 
113
  updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
114
  );
115
 
116
+ CREATE INDEX IF NOT EXISTS idx_kaiapi_provider_states_provider_id ON kaiapi_provider_states(provider_id);
117
+ CREATE INDEX IF NOT EXISTS idx_kaiapi_provider_states_enabled ON kaiapi_provider_states(enabled);
118
 
119
  -- Insert default providers (if table is empty)
120
+ INSERT INTO kaiapi_provider_states (provider_id, name, type, enabled) VALUES
121
  ('g4f', 'G4F (Free GPT-4)', 'api', true),
122
  ('zai', 'Z.ai (GLM-5)', 'api', true),
123
  ('gemini', 'Google Gemini', 'api', true),
 
141
  $$ language 'plpgsql';
142
 
143
  -- Create triggers for all tables
144
+ DROP TRIGGER IF EXISTS update_kaiapi_api_keys_updated_at ON kaiapi_api_keys;
145
+ CREATE TRIGGER update_kaiapi_api_keys_updated_at
146
+ BEFORE UPDATE ON kaiapi_api_keys
147
  FOR EACH ROW
148
  EXECUTE FUNCTION update_updated_at_column();
149
 
150
+ DROP TRIGGER IF EXISTS update_kaiapi_model_stats_updated_at ON kaiapi_model_stats;
151
+ CREATE TRIGGER update_kaiapi_model_stats_updated_at
152
+ BEFORE UPDATE ON kaiapi_model_stats
153
  FOR EACH ROW
154
  EXECUTE FUNCTION update_updated_at_column();
155
 
156
+ DROP TRIGGER IF EXISTS update_kaiapi_provider_sessions_updated_at ON kaiapi_provider_sessions;
157
+ CREATE TRIGGER update_kaiapi_provider_sessions_updated_at
158
+ BEFORE UPDATE ON kaiapi_provider_sessions
159
  FOR EACH ROW
160
  EXECUTE FUNCTION update_updated_at_column();
161
 
162
+ DROP TRIGGER IF EXISTS update_kaiapi_provider_states_updated_at ON kaiapi_provider_states;
163
+ CREATE TRIGGER update_kaiapi_provider_states_updated_at
164
+ BEFORE UPDATE ON kaiapi_provider_states
165
  FOR EACH ROW
166
  EXECUTE FUNCTION update_updated_at_column();
167
 
 
170
  -- ============================================
171
  -- Uncomment the following lines if you want to enable RLS
172
 
173
+ -- ALTER TABLE kaiapi_api_keys ENABLE ROW LEVEL SECURITY;
174
+ -- ALTER TABLE kaiapi_model_stats ENABLE ROW LEVEL SECURITY;
175
+ -- ALTER TABLE kaiapi_provider_sessions ENABLE ROW LEVEL SECURITY;
176
+ -- ALTER TABLE kaiapi_provider_states ENABLE ROW LEVEL SECURITY;
177
 
178
  -- Create policy to allow all operations (adjust as needed)
179
+ -- CREATE POLICY "Allow all operations on kaiapi_api_keys"
180
+ -- ON kaiapi_api_keys
181
  -- FOR ALL
182
  -- TO anon, authenticated
183
  -- USING (true)
184
  -- WITH CHECK (true);
185
 
186
+ -- CREATE POLICY "Allow all operations on kaiapi_model_stats"
187
+ -- ON kaiapi_model_stats
188
  -- FOR ALL
189
  -- TO anon, authenticated
190
  -- USING (true)
191
  -- WITH CHECK (true);
192
 
193
+ -- CREATE POLICY "Allow all operations on kaiapi_provider_sessions"
194
+ -- ON kaiapi_provider_sessions
195
  -- FOR ALL
196
  -- TO anon, authenticated
197
  -- USING (true)
198
  -- WITH CHECK (true);
199
 
200
+ -- CREATE POLICY "Allow all operations on kaiapi_provider_states"
201
+ -- ON kaiapi_provider_states
202
  -- FOR ALL
203
  -- TO anon, authenticated
204
  -- USING (true)
 
211
  SELECT table_name
212
  FROM information_schema.tables
213
  WHERE table_schema = 'public'
214
+ AND table_name LIKE 'kaiapi_%'
215
  ORDER BY table_name;
v1_router.py CHANGED
@@ -73,10 +73,10 @@ def update_usage_stats(key_id: str, tokens: int):
73
  supabase = get_supabase()
74
  if supabase and tokens > 0:
75
  try:
76
- current = supabase.table("KAIAPI_api_keys").select("usage_tokens").eq("id", key_id).execute()
77
  if current.data:
78
  new_total = (current.data[0]['usage_tokens'] or 0) + tokens
79
- supabase.table("KAIAPI_api_keys").update({"usage_tokens": new_total}).eq("id", key_id).execute()
80
 
81
  except Exception as e:
82
  print(f"Failed to update usage for {key_id}: {e}")
 
73
  supabase = get_supabase()
74
  if supabase and tokens > 0:
75
  try:
76
+ current = supabase.table("kaiapi_api_keys").select("usage_tokens").eq("id", key_id).execute()
77
  if current.data:
78
  new_total = (current.data[0]['usage_tokens'] or 0) + tokens
79
+ supabase.table("kaiapi_api_keys").update({"usage_tokens": new_total}).eq("id", key_id).execute()
80
 
81
  except Exception as e:
82
  print(f"Failed to update usage for {key_id}: {e}")