@axiom-lattice/gateway 2.1.12 → 2.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +8 -8
- package/CHANGELOG.md +10 -0
- package/README.md +53 -0
- package/dist/index.js +379 -50
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +377 -48
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
- package/src/config.ts +124 -0
- package/src/controllers/config.ts +126 -0
- package/src/controllers/models.ts +152 -0
- package/src/routes/index.ts +26 -0
- package/src/schemas/index.ts +74 -0
- package/src/services/supabase.ts +44 -10
package/src/config.ts
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration service
|
|
3
|
+
* Manages environment variables and supports dynamic updates
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface GatewayConfig {
|
|
7
|
+
port?: number;
|
|
8
|
+
queueServiceType?: string;
|
|
9
|
+
redisUrl?: string;
|
|
10
|
+
redisPassword?: string;
|
|
11
|
+
queueName?: string;
|
|
12
|
+
[key: string]: any; // Allow additional config keys
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Get configuration from environment variables
|
|
17
|
+
* Supports dynamic updates via updateConfig method
|
|
18
|
+
*/
|
|
19
|
+
class ConfigService {
|
|
20
|
+
private config: GatewayConfig;
|
|
21
|
+
|
|
22
|
+
constructor() {
|
|
23
|
+
this.config = this.loadFromEnv();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Load configuration from environment variables
|
|
28
|
+
*/
|
|
29
|
+
private loadFromEnv(): GatewayConfig {
|
|
30
|
+
return {
|
|
31
|
+
port: process.env.PORT ? Number(process.env.PORT) : undefined,
|
|
32
|
+
queueServiceType: process.env.QUEUE_SERVICE_TYPE,
|
|
33
|
+
redisUrl: process.env.REDIS_URL,
|
|
34
|
+
redisPassword: process.env.REDIS_PASSWORD,
|
|
35
|
+
queueName: process.env.QUEUE_NAME,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Update configuration from JSON object
|
|
41
|
+
* This will update both the internal config and process.env
|
|
42
|
+
*/
|
|
43
|
+
updateConfig(jsonConfig: Record<string, any>): void {
|
|
44
|
+
// Update process.env for all provided keys
|
|
45
|
+
for (const [key, value] of Object.entries(jsonConfig)) {
|
|
46
|
+
if (value !== null && value !== undefined) {
|
|
47
|
+
// Convert nested objects to environment variable format
|
|
48
|
+
if (typeof value === "object" && !Array.isArray(value)) {
|
|
49
|
+
// Handle nested objects like supabase: { url: "...", key: "..." }
|
|
50
|
+
for (const [nestedKey, nestedValue] of Object.entries(value)) {
|
|
51
|
+
const envKey = `${key.toUpperCase()}_${nestedKey.toUpperCase()}`;
|
|
52
|
+
process.env[envKey] = String(nestedValue);
|
|
53
|
+
}
|
|
54
|
+
} else {
|
|
55
|
+
// Handle flat keys
|
|
56
|
+
process.env[key.toUpperCase()] = String(value);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Reload config from updated environment variables
|
|
62
|
+
this.config = this.loadFromEnv();
|
|
63
|
+
|
|
64
|
+
// Deep merge the JSON config into our config object
|
|
65
|
+
this.config = this.deepMerge(this.config, jsonConfig);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Deep merge two objects
|
|
70
|
+
*/
|
|
71
|
+
private deepMerge(target: any, source: any): any {
|
|
72
|
+
const output = { ...target };
|
|
73
|
+
if (this.isObject(target) && this.isObject(source)) {
|
|
74
|
+
Object.keys(source).forEach((key) => {
|
|
75
|
+
if (this.isObject(source[key])) {
|
|
76
|
+
if (!(key in target)) {
|
|
77
|
+
Object.assign(output, { [key]: source[key] });
|
|
78
|
+
} else {
|
|
79
|
+
output[key] = this.deepMerge(target[key], source[key]);
|
|
80
|
+
}
|
|
81
|
+
} else {
|
|
82
|
+
Object.assign(output, { [key]: source[key] });
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
return output;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Check if value is a plain object
|
|
91
|
+
*/
|
|
92
|
+
private isObject(item: any): boolean {
|
|
93
|
+
return item && typeof item === "object" && !Array.isArray(item);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Get current configuration
|
|
98
|
+
*/
|
|
99
|
+
getConfig(): GatewayConfig {
|
|
100
|
+
return { ...this.config };
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Export singleton instance
|
|
105
|
+
export const configService = new ConfigService();
|
|
106
|
+
|
|
107
|
+
// Export config getter for backward compatibility
|
|
108
|
+
export const config = {
|
|
109
|
+
get port() {
|
|
110
|
+
return configService.getConfig().port;
|
|
111
|
+
},
|
|
112
|
+
get queueServiceType() {
|
|
113
|
+
return configService.getConfig().queueServiceType;
|
|
114
|
+
},
|
|
115
|
+
get redisUrl() {
|
|
116
|
+
return configService.getConfig().redisUrl;
|
|
117
|
+
},
|
|
118
|
+
get redisPassword() {
|
|
119
|
+
return configService.getConfig().redisPassword;
|
|
120
|
+
},
|
|
121
|
+
get queueName() {
|
|
122
|
+
return configService.getConfig().queueName;
|
|
123
|
+
},
|
|
124
|
+
};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { FastifyRequest, FastifyReply } from "fastify";
|
|
2
|
+
import { configService } from "../config";
|
|
3
|
+
import {
|
|
4
|
+
setQueueServiceType,
|
|
5
|
+
QueueServiceType,
|
|
6
|
+
} from "../services/queue_service";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Configuration Controller
|
|
10
|
+
* Handles configuration updates from frontend
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
interface UpdateConfigRequest {
|
|
14
|
+
Body: {
|
|
15
|
+
config: Record<string, any>;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Update gateway configuration
|
|
21
|
+
* Accepts JSON config and loads it into environment variables
|
|
22
|
+
*/
|
|
23
|
+
export async function updateConfig(
|
|
24
|
+
request: FastifyRequest<UpdateConfigRequest>,
|
|
25
|
+
reply: FastifyReply
|
|
26
|
+
) {
|
|
27
|
+
try {
|
|
28
|
+
const { config: jsonConfig } = request.body;
|
|
29
|
+
|
|
30
|
+
if (!jsonConfig || typeof jsonConfig !== "object") {
|
|
31
|
+
return reply.status(400).send({
|
|
32
|
+
success: false,
|
|
33
|
+
error: "Invalid configuration: config must be an object",
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Update configuration service
|
|
38
|
+
configService.updateConfig(jsonConfig);
|
|
39
|
+
|
|
40
|
+
const warnings: string[] = [];
|
|
41
|
+
const requiresRestart: string[] = [];
|
|
42
|
+
|
|
43
|
+
// Check if port is being changed (requires restart)
|
|
44
|
+
if (jsonConfig.port !== undefined) {
|
|
45
|
+
requiresRestart.push("PORT");
|
|
46
|
+
warnings.push("Port change requires server restart to take effect");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// If queue service type is being updated, reconfigure the queue service
|
|
50
|
+
if (jsonConfig.queueServiceType) {
|
|
51
|
+
setQueueServiceType(jsonConfig.queueServiceType as QueueServiceType);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// If Redis configuration is being updated and queue service is Redis, reconfigure
|
|
55
|
+
if (
|
|
56
|
+
(jsonConfig.redisUrl || jsonConfig.redisPassword) &&
|
|
57
|
+
(process.env.QUEUE_SERVICE_TYPE === "redis" ||
|
|
58
|
+
jsonConfig.queueServiceType === "redis")
|
|
59
|
+
) {
|
|
60
|
+
// Reconfigure queue service to pick up new Redis settings
|
|
61
|
+
const currentType =
|
|
62
|
+
(jsonConfig.queueServiceType as QueueServiceType) ||
|
|
63
|
+
(process.env.QUEUE_SERVICE_TYPE as QueueServiceType) ||
|
|
64
|
+
"memory";
|
|
65
|
+
if (currentType === "redis") {
|
|
66
|
+
setQueueServiceType("redis");
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Get updated config (without sensitive data)
|
|
71
|
+
const updatedConfig = configService.getConfig();
|
|
72
|
+
const safeConfig = {
|
|
73
|
+
...updatedConfig,
|
|
74
|
+
redisPassword: updatedConfig.redisPassword
|
|
75
|
+
? "***"
|
|
76
|
+
: updatedConfig.redisPassword,
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
return reply.send({
|
|
80
|
+
success: true,
|
|
81
|
+
message: "Configuration updated successfully",
|
|
82
|
+
data: safeConfig,
|
|
83
|
+
warnings: warnings.length > 0 ? warnings : undefined,
|
|
84
|
+
requiresRestart: requiresRestart.length > 0 ? requiresRestart : undefined,
|
|
85
|
+
});
|
|
86
|
+
} catch (error: any) {
|
|
87
|
+
console.error("Failed to update configuration", {
|
|
88
|
+
error: error.message,
|
|
89
|
+
stack: error.stack,
|
|
90
|
+
});
|
|
91
|
+
return reply.status(500).send({
|
|
92
|
+
success: false,
|
|
93
|
+
error: error.message || "Failed to update configuration",
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Get current gateway configuration
|
|
100
|
+
* Returns current configuration (with sensitive data masked)
|
|
101
|
+
*/
|
|
102
|
+
export async function getConfig(request: FastifyRequest, reply: FastifyReply) {
|
|
103
|
+
try {
|
|
104
|
+
const currentConfig = configService.getConfig();
|
|
105
|
+
const safeConfig = {
|
|
106
|
+
...currentConfig,
|
|
107
|
+
redisPassword: currentConfig.redisPassword
|
|
108
|
+
? "***"
|
|
109
|
+
: currentConfig.redisPassword,
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
return reply.send({
|
|
113
|
+
success: true,
|
|
114
|
+
data: safeConfig,
|
|
115
|
+
});
|
|
116
|
+
} catch (error: any) {
|
|
117
|
+
console.error("Failed to get configuration", {
|
|
118
|
+
error: error.message,
|
|
119
|
+
stack: error.stack,
|
|
120
|
+
});
|
|
121
|
+
return reply.status(500).send({
|
|
122
|
+
success: false,
|
|
123
|
+
error: error.message || "Failed to get configuration",
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { FastifyRequest, FastifyReply } from "fastify";
|
|
2
|
+
import { registerModelLattice, modelLatticeManager } from "@axiom-lattice/core";
|
|
3
|
+
import type { LLMConfig } from "@axiom-lattice/protocols";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Models Controller
|
|
7
|
+
* Handles model lattice registration and management
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
interface ModelConfig {
|
|
11
|
+
key: string;
|
|
12
|
+
model: string;
|
|
13
|
+
provider: "azure" | "openai" | "deepseek" | "siliconcloud" | "volcengine";
|
|
14
|
+
streaming?: boolean;
|
|
15
|
+
apiKey?: string;
|
|
16
|
+
baseURL?: string;
|
|
17
|
+
maxTokens?: number;
|
|
18
|
+
temperature?: number;
|
|
19
|
+
timeout?: number;
|
|
20
|
+
maxRetries?: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface UpdateModelsRequest {
|
|
24
|
+
Body: {
|
|
25
|
+
models: ModelConfig[];
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Get all registered models
|
|
31
|
+
*/
|
|
32
|
+
export async function getModels(request: FastifyRequest, reply: FastifyReply) {
|
|
33
|
+
try {
|
|
34
|
+
const allLattices = modelLatticeManager.getAllLattices();
|
|
35
|
+
const models = allLattices.map((lattice) => {
|
|
36
|
+
// Extract config from the lattice client
|
|
37
|
+
// Note: This is a simplified approach - you may need to adjust based on actual implementation
|
|
38
|
+
const config = (lattice.client as any).config || {};
|
|
39
|
+
return {
|
|
40
|
+
key: lattice.key,
|
|
41
|
+
model: config.model || "",
|
|
42
|
+
provider: config.provider || "openai",
|
|
43
|
+
streaming: config.streaming || false,
|
|
44
|
+
apiKey: config.apiKey || "",
|
|
45
|
+
baseURL: config.baseURL || "",
|
|
46
|
+
maxTokens: config.maxTokens,
|
|
47
|
+
temperature: config.temperature,
|
|
48
|
+
timeout: config.timeout,
|
|
49
|
+
maxRetries: config.maxRetries,
|
|
50
|
+
};
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
return reply.send({
|
|
54
|
+
success: true,
|
|
55
|
+
data: models,
|
|
56
|
+
});
|
|
57
|
+
} catch (error: any) {
|
|
58
|
+
console.error("Failed to get models", {
|
|
59
|
+
error: error.message,
|
|
60
|
+
stack: error.stack,
|
|
61
|
+
});
|
|
62
|
+
return reply.status(500).send({
|
|
63
|
+
success: false,
|
|
64
|
+
error: error.message || "Failed to get models",
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Update models configuration
|
|
71
|
+
* Registers or updates model lattices
|
|
72
|
+
*/
|
|
73
|
+
export async function updateModels(
|
|
74
|
+
request: FastifyRequest<UpdateModelsRequest>,
|
|
75
|
+
reply: FastifyReply
|
|
76
|
+
) {
|
|
77
|
+
try {
|
|
78
|
+
const { models } = request.body;
|
|
79
|
+
|
|
80
|
+
if (!models || !Array.isArray(models)) {
|
|
81
|
+
return reply.status(400).send({
|
|
82
|
+
success: false,
|
|
83
|
+
error: "Invalid request: models must be an array",
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const registeredModels: string[] = [];
|
|
88
|
+
const errors: string[] = [];
|
|
89
|
+
|
|
90
|
+
for (const modelConfig of models) {
|
|
91
|
+
if (!modelConfig.key || !modelConfig.model || !modelConfig.provider) {
|
|
92
|
+
errors.push(
|
|
93
|
+
`Model configuration is incomplete: key, model, and provider are required`
|
|
94
|
+
);
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
// Remove existing model with the same key if it exists
|
|
100
|
+
if (modelLatticeManager.hasLattice(modelConfig.key)) {
|
|
101
|
+
modelLatticeManager.removeLattice(modelConfig.key);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Convert to LLMConfig format
|
|
105
|
+
const llmConfig: LLMConfig = {
|
|
106
|
+
provider: modelConfig.provider,
|
|
107
|
+
model: modelConfig.model,
|
|
108
|
+
streaming: modelConfig.streaming ?? false,
|
|
109
|
+
apiKey: modelConfig.apiKey,
|
|
110
|
+
baseURL: modelConfig.baseURL,
|
|
111
|
+
maxTokens: modelConfig.maxTokens,
|
|
112
|
+
temperature: modelConfig.temperature,
|
|
113
|
+
timeout: modelConfig.timeout,
|
|
114
|
+
maxRetries: modelConfig.maxRetries,
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
// Register the new model lattice
|
|
118
|
+
registerModelLattice(modelConfig.key, llmConfig);
|
|
119
|
+
registeredModels.push(modelConfig.key);
|
|
120
|
+
} catch (error: any) {
|
|
121
|
+
errors.push(
|
|
122
|
+
`Failed to register model ${modelConfig.key}: ${error.message}`
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (errors.length > 0 && registeredModels.length === 0) {
|
|
128
|
+
return reply.status(400).send({
|
|
129
|
+
success: false,
|
|
130
|
+
error: errors.join("; "),
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return reply.send({
|
|
135
|
+
success: true,
|
|
136
|
+
message: `Successfully registered ${registeredModels.length} model(s)`,
|
|
137
|
+
data: {
|
|
138
|
+
registered: registeredModels,
|
|
139
|
+
errors: errors.length > 0 ? errors : undefined,
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
} catch (error: any) {
|
|
143
|
+
console.error("Failed to update models", {
|
|
144
|
+
error: error.message,
|
|
145
|
+
stack: error.stack,
|
|
146
|
+
});
|
|
147
|
+
return reply.status(500).send({
|
|
148
|
+
success: false,
|
|
149
|
+
error: error.message || "Failed to update models",
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
}
|
package/src/routes/index.ts
CHANGED
|
@@ -5,6 +5,8 @@ import * as memoryController from "../controllers/memory";
|
|
|
5
5
|
import * as graphController from "../controllers/assistant";
|
|
6
6
|
import * as agentTaskController from "../controllers/agent_task";
|
|
7
7
|
import * as threadsController from "../controllers/threads";
|
|
8
|
+
import * as configController from "../controllers/config";
|
|
9
|
+
import * as modelsController from "../controllers/models";
|
|
8
10
|
import {
|
|
9
11
|
createRunSchema,
|
|
10
12
|
getAllMemoryItemsSchema,
|
|
@@ -16,6 +18,8 @@ import {
|
|
|
16
18
|
getAgentGraphSchema,
|
|
17
19
|
resumeStreamSchema,
|
|
18
20
|
triggerAgentTaskSchema,
|
|
21
|
+
updateConfigSchema,
|
|
22
|
+
getConfigSchema,
|
|
19
23
|
} from "../schemas";
|
|
20
24
|
|
|
21
25
|
export const registerLatticeRoutes = (app: FastifyInstance): void => {
|
|
@@ -160,4 +164,26 @@ export const registerLatticeRoutes = (app: FastifyInstance): void => {
|
|
|
160
164
|
"/api/assistants/:assistantId/threads/:threadId",
|
|
161
165
|
threadsController.deleteThread
|
|
162
166
|
);
|
|
167
|
+
|
|
168
|
+
// Configuration routes
|
|
169
|
+
app.get(
|
|
170
|
+
"/api/config",
|
|
171
|
+
{ schema: getConfigSchema },
|
|
172
|
+
configController.getConfig
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
app.put<{
|
|
176
|
+
Body: { config: Record<string, any> };
|
|
177
|
+
}>(
|
|
178
|
+
"/api/config",
|
|
179
|
+
{ schema: updateConfigSchema },
|
|
180
|
+
configController.updateConfig
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
// Models routes
|
|
184
|
+
app.get("/api/models", modelsController.getModels);
|
|
185
|
+
|
|
186
|
+
app.put<{
|
|
187
|
+
Body: { models: any[] };
|
|
188
|
+
}>("/api/models", modelsController.updateModels);
|
|
163
189
|
};
|
package/src/schemas/index.ts
CHANGED
|
@@ -245,3 +245,77 @@ export const triggerAgentTaskSchema: FastifySchema = {
|
|
|
245
245
|
},
|
|
246
246
|
},
|
|
247
247
|
};
|
|
248
|
+
|
|
249
|
+
// Configuration Schemas
|
|
250
|
+
export const updateConfigSchema: FastifySchema = {
|
|
251
|
+
description: "Update gateway configuration",
|
|
252
|
+
tags: ["Configuration"],
|
|
253
|
+
summary: "Update Configuration",
|
|
254
|
+
body: {
|
|
255
|
+
type: "object",
|
|
256
|
+
properties: {
|
|
257
|
+
config: {
|
|
258
|
+
type: "object",
|
|
259
|
+
description: "Configuration object to update",
|
|
260
|
+
properties: {
|
|
261
|
+
port: { type: "number", description: "Server port" },
|
|
262
|
+
queueServiceType: {
|
|
263
|
+
type: "string",
|
|
264
|
+
enum: ["memory", "redis"],
|
|
265
|
+
description: "Queue service type",
|
|
266
|
+
},
|
|
267
|
+
redisUrl: { type: "string", description: "Redis URL" },
|
|
268
|
+
redisPassword: { type: "string", description: "Redis password" },
|
|
269
|
+
queueName: { type: "string", description: "Queue name" },
|
|
270
|
+
},
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
required: ["config"],
|
|
274
|
+
},
|
|
275
|
+
response: {
|
|
276
|
+
200: {
|
|
277
|
+
type: "object",
|
|
278
|
+
properties: {
|
|
279
|
+
success: { type: "boolean" },
|
|
280
|
+
message: { type: "string" },
|
|
281
|
+
data: { type: "object" },
|
|
282
|
+
},
|
|
283
|
+
},
|
|
284
|
+
400: {
|
|
285
|
+
type: "object",
|
|
286
|
+
properties: {
|
|
287
|
+
success: { type: "boolean" },
|
|
288
|
+
error: { type: "string" },
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
500: {
|
|
292
|
+
type: "object",
|
|
293
|
+
properties: {
|
|
294
|
+
success: { type: "boolean" },
|
|
295
|
+
error: { type: "string" },
|
|
296
|
+
},
|
|
297
|
+
},
|
|
298
|
+
},
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
export const getConfigSchema: FastifySchema = {
|
|
302
|
+
description: "Get current gateway configuration",
|
|
303
|
+
tags: ["Configuration"],
|
|
304
|
+
summary: "Get Configuration",
|
|
305
|
+
response: {
|
|
306
|
+
200: {
|
|
307
|
+
type: "object",
|
|
308
|
+
properties: {
|
|
309
|
+
success: { type: "boolean" },
|
|
310
|
+
data: { type: "object" },
|
|
311
|
+
},
|
|
312
|
+
},
|
|
313
|
+
500: {
|
|
314
|
+
type: "object",
|
|
315
|
+
properties: {
|
|
316
|
+
success: { type: "boolean" },
|
|
317
|
+
error: { type: "string" },
|
|
318
|
+
},
|
|
319
|
+
},
|
|
320
|
+
},
|
|
321
|
+
};
|
package/src/services/supabase.ts
CHANGED
|
@@ -1,15 +1,36 @@
|
|
|
1
|
-
import { createClient } from "@supabase/supabase-js";
|
|
2
|
-
import { config } from "../config";
|
|
1
|
+
import { createClient, SupabaseClient } from "@supabase/supabase-js";
|
|
3
2
|
// import { Database } from "@/types/database.types";
|
|
4
3
|
// import { Database as PgmqPublic } from "@/types/pgmq_public.types";
|
|
5
|
-
// 创建Supabase客户端
|
|
6
|
-
const supabaseClient = createClient(config.supabase.url, config.supabase.key);
|
|
7
4
|
|
|
8
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Get Supabase client
|
|
7
|
+
* This function reads from environment variables directly
|
|
8
|
+
*/
|
|
9
|
+
const getSupabaseClient = (): SupabaseClient => {
|
|
10
|
+
const url = process.env.SUPABASE_URL;
|
|
11
|
+
const key = process.env.SUPABASE_KEY;
|
|
12
|
+
if (!url || !key) {
|
|
13
|
+
throw new Error("Supabase URL and Key must be configured");
|
|
14
|
+
}
|
|
15
|
+
return createClient(url, key);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// Default export - creates client using current config
|
|
19
|
+
// Note: Since config uses getters, this will read the latest config values
|
|
20
|
+
export default getSupabaseClient();
|
|
9
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Create a Supabase client with tenant headers
|
|
24
|
+
* This function reads from environment variables directly
|
|
25
|
+
*/
|
|
10
26
|
export const createSupabaseClient = (headers: Record<string, string>) => {
|
|
11
27
|
const currentTenantId = headers["x-tenant-id"];
|
|
12
|
-
|
|
28
|
+
const url = process.env.SUPABASE_URL;
|
|
29
|
+
const key = process.env.SUPABASE_KEY;
|
|
30
|
+
if (!url || !key) {
|
|
31
|
+
throw new Error("Supabase URL and Key must be configured");
|
|
32
|
+
}
|
|
33
|
+
return createClient(url, key, {
|
|
13
34
|
global: {
|
|
14
35
|
fetch: async (input, init) => {
|
|
15
36
|
const headers = new Headers(init?.headers);
|
|
@@ -20,7 +41,20 @@ export const createSupabaseClient = (headers: Record<string, string>) => {
|
|
|
20
41
|
});
|
|
21
42
|
};
|
|
22
43
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
44
|
+
/**
|
|
45
|
+
* Get Supabase queue client
|
|
46
|
+
* This function reads from environment variables directly
|
|
47
|
+
*/
|
|
48
|
+
export const getSupabaseQueueClient = (): SupabaseClient => {
|
|
49
|
+
const url = process.env.SUPABASE_URL;
|
|
50
|
+
const key = process.env.SUPABASE_KEY;
|
|
51
|
+
if (!url || !key) {
|
|
52
|
+
throw new Error("Supabase URL and Key must be configured");
|
|
53
|
+
}
|
|
54
|
+
return createClient(url, key);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// For backward compatibility, export a client that reads config dynamically
|
|
58
|
+
// Note: This creates a new client each time, but Supabase clients are lightweight
|
|
59
|
+
// and this ensures we always use the latest config
|
|
60
|
+
export const supabaseQueueClient = getSupabaseQueueClient();
|