Spaces:
Running
Running
fix: use lowercase table names for PostgreSQL compatibility
Browse filesPostgreSQL 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.
- admin_router.py +5 -5
- auth.py +1 -1
- engine.py +4 -4
- provider_sessions.py +4 -4
- provider_state.py +4 -4
- supabase_setup.sql +50 -50
- 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("
|
| 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("
|
| 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("
|
| 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("
|
| 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("
|
| 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("
|
| 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("
|
| 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("
|
| 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("
|
| 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("
|
| 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("
|
| 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("
|
| 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("
|
| 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("
|
| 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
|
| 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
|
| 16 |
-
TABLE_NAME = "
|
| 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
|
| 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
|
| 5 |
-- and migrating data from old tables (if they exist)
|
| 6 |
|
| 7 |
-- ============================================
|
| 8 |
-
-- STEP 1: Rename existing tables to add
|
| 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
|
| 18 |
-
RAISE NOTICE 'Renamed api_keys to
|
| 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
|
| 29 |
-
RAISE NOTICE 'Renamed model_stats to
|
| 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
|
| 40 |
-
RAISE NOTICE 'Renamed provider_sessions to
|
| 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
|
| 51 |
-
RAISE NOTICE 'Renamed provider_states to
|
| 52 |
END IF;
|
| 53 |
END $$;
|
| 54 |
|
| 55 |
-- ============================================
|
| 56 |
-
-- STEP 2: Create
|
| 57 |
-- ============================================
|
| 58 |
-
CREATE TABLE IF NOT EXISTS
|
| 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
|
| 70 |
-
CREATE INDEX IF NOT EXISTS
|
| 71 |
|
| 72 |
-- ============================================
|
| 73 |
-
-- STEP 3: Create
|
| 74 |
-- ============================================
|
| 75 |
-
CREATE TABLE IF NOT EXISTS
|
| 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
|
| 88 |
|
| 89 |
-- ============================================
|
| 90 |
-
-- STEP 4: Create
|
| 91 |
-- ============================================
|
| 92 |
-
CREATE TABLE IF NOT EXISTS
|
| 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
|
| 102 |
|
| 103 |
-- ============================================
|
| 104 |
-
-- STEP 5: Create
|
| 105 |
-- ============================================
|
| 106 |
-
CREATE TABLE IF NOT EXISTS
|
| 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
|
| 117 |
-
CREATE INDEX IF NOT EXISTS
|
| 118 |
|
| 119 |
-- Insert default providers (if table is empty)
|
| 120 |
-
INSERT INTO
|
| 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
|
| 145 |
-
CREATE TRIGGER
|
| 146 |
-
BEFORE UPDATE ON
|
| 147 |
FOR EACH ROW
|
| 148 |
EXECUTE FUNCTION update_updated_at_column();
|
| 149 |
|
| 150 |
-
DROP TRIGGER IF EXISTS
|
| 151 |
-
CREATE TRIGGER
|
| 152 |
-
BEFORE UPDATE ON
|
| 153 |
FOR EACH ROW
|
| 154 |
EXECUTE FUNCTION update_updated_at_column();
|
| 155 |
|
| 156 |
-
DROP TRIGGER IF EXISTS
|
| 157 |
-
CREATE TRIGGER
|
| 158 |
-
BEFORE UPDATE ON
|
| 159 |
FOR EACH ROW
|
| 160 |
EXECUTE FUNCTION update_updated_at_column();
|
| 161 |
|
| 162 |
-
DROP TRIGGER IF EXISTS
|
| 163 |
-
CREATE TRIGGER
|
| 164 |
-
BEFORE UPDATE ON
|
| 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
|
| 174 |
-
-- ALTER TABLE
|
| 175 |
-
-- ALTER TABLE
|
| 176 |
-
-- ALTER TABLE
|
| 177 |
|
| 178 |
-- Create policy to allow all operations (adjust as needed)
|
| 179 |
-
-- CREATE POLICY "Allow all operations on
|
| 180 |
-
-- ON
|
| 181 |
-- FOR ALL
|
| 182 |
-- TO anon, authenticated
|
| 183 |
-- USING (true)
|
| 184 |
-- WITH CHECK (true);
|
| 185 |
|
| 186 |
-
-- CREATE POLICY "Allow all operations on
|
| 187 |
-
-- ON
|
| 188 |
-- FOR ALL
|
| 189 |
-- TO anon, authenticated
|
| 190 |
-- USING (true)
|
| 191 |
-- WITH CHECK (true);
|
| 192 |
|
| 193 |
-
-- CREATE POLICY "Allow all operations on
|
| 194 |
-
-- ON
|
| 195 |
-- FOR ALL
|
| 196 |
-- TO anon, authenticated
|
| 197 |
-- USING (true)
|
| 198 |
-- WITH CHECK (true);
|
| 199 |
|
| 200 |
-
-- CREATE POLICY "Allow all operations on
|
| 201 |
-
-- ON
|
| 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 '
|
| 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("
|
| 77 |
if current.data:
|
| 78 |
new_total = (current.data[0]['usage_tokens'] or 0) + tokens
|
| 79 |
-
supabase.table("
|
| 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}")
|