|
1 | 1 | import { Resource } from "@opencode-ai/console-resource" |
2 | | -import { and, Database, eq, isNull, sql } from "@opencode-ai/console-core/drizzle/index.js" |
| 2 | +import { and, Database, eq, isNotNull, isNull, sql } from "@opencode-ai/console-core/drizzle/index.js" |
3 | 3 | import { KeyTable } from "@opencode-ai/console-core/schema/key.sql.js" |
4 | 4 | import { ProviderTable } from "@opencode-ai/console-core/schema/provider.sql.js" |
5 | 5 | import { WorkspaceTable } from "@opencode-ai/console-core/schema/workspace.sql.js" |
@@ -34,52 +34,30 @@ export async function proxyInference( |
34 | 34 | : undefined) |
35 | 35 | if (!path) return undefined |
36 | 36 |
|
37 | | - const go = url.pathname.startsWith("/zen/go/") |
38 | 37 | const key = url.pathname.endsWith("/messages") |
39 | 38 | ? request.headers.get("x-api-key") |
40 | 39 | : path.startsWith("/google/") |
41 | 40 | ? request.headers.get("x-goog-api-key") |
42 | 41 | : request.headers.get("authorization")?.split(" ")[1] |
43 | 42 | if (!key || key === "public") return undefined |
44 | 43 |
|
45 | | - // Routing only; the destination owns authentication and revocation after cutover. |
46 | | - const workspace = await Database.use((tx) => |
47 | | - tx |
48 | | - .select({ |
49 | | - id: WorkspaceTable.id, |
50 | | - migratedAt: WorkspaceTable.migrated_at, |
51 | | - provider: ProviderTable.provider, |
52 | | - }) |
53 | | - .from(KeyTable) |
54 | | - .innerJoin(WorkspaceTable, eq(WorkspaceTable.id, KeyTable.workspaceID)) |
55 | | - .leftJoin( |
56 | | - ProviderTable, |
57 | | - !go && generation?.provider |
58 | | - ? and( |
59 | | - eq(ProviderTable.workspaceID, KeyTable.workspaceID), |
60 | | - eq(ProviderTable.provider, generation.provider), |
61 | | - isNull(ProviderTable.timeDeleted), |
62 | | - sql`length(${ProviderTable.credentials}) > 0`, |
63 | | - ) |
64 | | - : sql`false`, |
65 | | - ) |
66 | | - .where(eq(KeyTable.key, key)) |
67 | | - .limit(1) |
68 | | - .then((rows) => rows[0]), |
69 | | - ) |
70 | | - if (!workspace?.migratedAt) return undefined |
71 | | - const model = workspace.provider ? generation?.model : undefined |
72 | | - if (workspace.provider && !model) throw new Error("Legacy BYOK model mapping is unavailable") |
| 44 | + // New Console keys are never in the legacy key table; every legacy key is `sk-`. |
| 45 | + const legacy = !key.startsWith("oc_sk_") |
| 46 | + const workspace = legacy ? await migratedWorkspace(key, generation?.provider) : undefined |
| 47 | + if (legacy && !workspace) return undefined |
| 48 | + const model = workspace?.provider ? generation?.model : undefined |
| 49 | + if (workspace?.provider && !model) throw new Error("Legacy BYOK model mapping is unavailable") |
73 | 50 |
|
74 | 51 | const destination = new URL(Resource.ConsoleMigration.inferenceUrl) |
75 | 52 | // Imported connections must use this same workspace/provider-derived ID. |
76 | | - const target = model |
77 | | - ? `/custom/conn_${workspace.id.slice(4)}_${workspace.provider}${ |
78 | | - path.startsWith("/google/") |
79 | | - ? `/models/${encodeURIComponent(model)}${url.pathname.slice(url.pathname.lastIndexOf(":"))}` |
80 | | - : url.pathname.slice("/zen/v1".length) |
81 | | - }` |
82 | | - : path |
| 53 | + const target = |
| 54 | + model && workspace |
| 55 | + ? `/custom/conn_${workspace.id.slice(4)}_${workspace.provider}${ |
| 56 | + path.startsWith("/google/") |
| 57 | + ? `/models/${encodeURIComponent(model)}${url.pathname.slice(url.pathname.lastIndexOf(":"))}` |
| 58 | + : url.pathname.slice("/zen/v1".length) |
| 59 | + }` |
| 60 | + : path |
83 | 61 | destination.pathname = `${destination.pathname.replace(/\/$/, "")}${target}` |
84 | 62 | destination.search = url.search |
85 | 63 | destination.hash = "" |
@@ -109,6 +87,30 @@ export async function proxyInference( |
109 | 87 | return fetch(forwarded, { redirect: "manual" }) |
110 | 88 | } |
111 | 89 |
|
| 90 | +// Routing only; the destination owns authentication and revocation after cutover. |
| 91 | +function migratedWorkspace(key: string, provider?: string) { |
| 92 | + return Database.use((tx) => |
| 93 | + tx |
| 94 | + .select({ id: WorkspaceTable.id, provider: ProviderTable.provider }) |
| 95 | + .from(KeyTable) |
| 96 | + .innerJoin(WorkspaceTable, eq(WorkspaceTable.id, KeyTable.workspaceID)) |
| 97 | + .leftJoin( |
| 98 | + ProviderTable, |
| 99 | + provider |
| 100 | + ? and( |
| 101 | + eq(ProviderTable.workspaceID, KeyTable.workspaceID), |
| 102 | + eq(ProviderTable.provider, provider), |
| 103 | + isNull(ProviderTable.timeDeleted), |
| 104 | + sql`length(${ProviderTable.credentials}) > 0`, |
| 105 | + ) |
| 106 | + : sql`false`, |
| 107 | + ) |
| 108 | + .where(and(eq(KeyTable.key, key), isNotNull(WorkspaceTable.migrated_at))) |
| 109 | + .limit(1) |
| 110 | + .then((rows) => rows[0]), |
| 111 | + ) |
| 112 | +} |
| 113 | + |
112 | 114 | export function inferenceUnavailable() { |
113 | 115 | return Response.json( |
114 | 116 | { error: { type: "api_error", message: "Inference routing is unavailable. Please retry later." } }, |
|
0 commit comments