mirror of
https://github.com/anomalyco/opencode.git
synced 2026-07-10 02:58:29 +00:00
zen: track region
This commit is contained in:
parent
d71454c701
commit
e9d4ca7e0f
27 changed files with 3343 additions and 9 deletions
|
|
@ -646,6 +646,9 @@ export const dict = {
|
|||
"workspace.lite.subscription.useBalance": "استخدم رصيدك المتوفر بعد الوصول إلى حدود الاستخدام",
|
||||
"workspace.lite.subscription.selectProvider":
|
||||
'اختر "OpenCode Go" كمزود في إعدادات opencode الخاصة بك لاستخدام نماذج Go.',
|
||||
"workspace.lite.providers.title": "المزودون",
|
||||
"workspace.lite.providers.description": "تحكم في المزودين المستخدمين للتوجيه.",
|
||||
"workspace.lite.providers.useChina": "تفعيل النماذج المستضافة في الصين",
|
||||
"workspace.lite.black.message":
|
||||
"أنت مشترك حاليًا في OpenCode Black أو في قائمة الانتظار. يرجى إلغاء الاشتراك أولاً إذا كنت ترغب في التبديل إلى Go.",
|
||||
"workspace.lite.other.message":
|
||||
|
|
|
|||
|
|
@ -656,6 +656,9 @@ export const dict = {
|
|||
"workspace.lite.subscription.useBalance": "Use seu saldo disponível após atingir os limites de uso",
|
||||
"workspace.lite.subscription.selectProvider":
|
||||
'Selecione "OpenCode Go" como provedor na sua configuração do opencode para usar os modelos Go.',
|
||||
"workspace.lite.providers.title": "Provedores",
|
||||
"workspace.lite.providers.description": "Controle quais provedores são usados para roteamento.",
|
||||
"workspace.lite.providers.useChina": "Ativar modelos hospedados na China",
|
||||
"workspace.lite.black.message":
|
||||
"Você está atualmente inscrito no OpenCode Black ou na lista de espera. Por favor, cancele a assinatura primeiro se desejar mudar para o Go.",
|
||||
"workspace.lite.other.message":
|
||||
|
|
|
|||
|
|
@ -652,6 +652,9 @@ export const dict = {
|
|||
"workspace.lite.subscription.useBalance": "Brug din tilgængelige saldo, når du har nået forbrugsgrænserne",
|
||||
"workspace.lite.subscription.selectProvider":
|
||||
'Vælg "OpenCode Go" som udbyder i din opencode-konfiguration for at bruge Go-modeller.',
|
||||
"workspace.lite.providers.title": "Udbydere",
|
||||
"workspace.lite.providers.description": "Styr, hvilke udbydere der bruges til routing.",
|
||||
"workspace.lite.providers.useChina": "Aktivér modeller hostet i Kina",
|
||||
"workspace.lite.black.message":
|
||||
"Du abonnerer i øjeblikket på OpenCode Black eller er på venteliste. Afmeld venligst først, hvis du vil skifte til Go.",
|
||||
"workspace.lite.other.message":
|
||||
|
|
|
|||
|
|
@ -655,6 +655,9 @@ export const dict = {
|
|||
"workspace.lite.subscription.useBalance": "Nutze dein verfügbares Guthaben, nachdem die Nutzungslimits erreicht sind",
|
||||
"workspace.lite.subscription.selectProvider":
|
||||
'Wähle "OpenCode Go" als Anbieter in deiner opencode-Konfiguration, um Go-Modelle zu verwenden.',
|
||||
"workspace.lite.providers.title": "Anbieter",
|
||||
"workspace.lite.providers.description": "Steuere, welche Anbieter für das Routing verwendet werden.",
|
||||
"workspace.lite.providers.useChina": "In China gehostete Modelle aktivieren",
|
||||
"workspace.lite.black.message":
|
||||
"Du hast derzeit OpenCode Black abonniert oder stehst auf der Warteliste. Bitte kündige zuerst, wenn du zu Go wechseln möchtest.",
|
||||
"workspace.lite.other.message":
|
||||
|
|
|
|||
|
|
@ -649,6 +649,9 @@ export const dict = {
|
|||
"workspace.lite.subscription.useBalance": "Use your available balance after reaching the usage limits",
|
||||
"workspace.lite.subscription.selectProvider":
|
||||
'Select "OpenCode Go" as the provider in your opencode configuration to use Go models.',
|
||||
"workspace.lite.providers.title": "Providers",
|
||||
"workspace.lite.providers.description": "Control which providers are used for routing.",
|
||||
"workspace.lite.providers.useChina": "Enable models hosted in China",
|
||||
"workspace.lite.black.message":
|
||||
"You're currently subscribed to OpenCode Black or on the waitlist. Please unsubscribe first if you'd like to switch to Go.",
|
||||
"workspace.lite.other.message":
|
||||
|
|
|
|||
|
|
@ -656,6 +656,9 @@ export const dict = {
|
|||
"workspace.lite.subscription.useBalance": "Usa tu saldo disponible después de alcanzar los límites de uso",
|
||||
"workspace.lite.subscription.selectProvider":
|
||||
'Selecciona "OpenCode Go" como proveedor en tu configuración de opencode para usar los modelos Go.',
|
||||
"workspace.lite.providers.title": "Proveedores",
|
||||
"workspace.lite.providers.description": "Controla qué proveedores se usan para el enrutamiento.",
|
||||
"workspace.lite.providers.useChina": "Activar modelos alojados en China",
|
||||
"workspace.lite.black.message":
|
||||
"Actualmente estás suscrito a OpenCode Black o estás en la lista de espera. Por favor, cancela la suscripción primero si deseas cambiar a Go.",
|
||||
"workspace.lite.other.message":
|
||||
|
|
|
|||
|
|
@ -662,6 +662,9 @@ export const dict = {
|
|||
"Utilisez votre solde disponible après avoir atteint les limites d'utilisation",
|
||||
"workspace.lite.subscription.selectProvider":
|
||||
'Sélectionnez "OpenCode Go" comme fournisseur dans votre configuration opencode pour utiliser les modèles Go.',
|
||||
"workspace.lite.providers.title": "Fournisseurs",
|
||||
"workspace.lite.providers.description": "Contrôlez les fournisseurs utilisés pour le routage.",
|
||||
"workspace.lite.providers.useChina": "Activer les modèles hébergés en Chine",
|
||||
"workspace.lite.black.message":
|
||||
"Vous êtes actuellement abonné à OpenCode Black ou sur liste d'attente. Veuillez d'abord vous désabonner si vous souhaitez passer à Go.",
|
||||
"workspace.lite.other.message":
|
||||
|
|
|
|||
|
|
@ -654,6 +654,9 @@ export const dict = {
|
|||
"workspace.lite.subscription.useBalance": "Usa il tuo saldo disponibile dopo aver raggiunto i limiti di utilizzo",
|
||||
"workspace.lite.subscription.selectProvider":
|
||||
'Seleziona "OpenCode Go" come provider nella tua configurazione opencode per utilizzare i modelli Go.',
|
||||
"workspace.lite.providers.title": "Provider",
|
||||
"workspace.lite.providers.description": "Controlla quali provider vengono usati per il routing.",
|
||||
"workspace.lite.providers.useChina": "Abilita modelli ospitati in Cina",
|
||||
"workspace.lite.black.message":
|
||||
"Attualmente sei abbonato a OpenCode Black o sei in lista d'attesa. Annulla l'iscrizione prima se desideri passare a Go.",
|
||||
"workspace.lite.other.message":
|
||||
|
|
|
|||
|
|
@ -654,6 +654,9 @@ export const dict = {
|
|||
"workspace.lite.subscription.useBalance": "利用限度額に達したら利用可能な残高を使用する",
|
||||
"workspace.lite.subscription.selectProvider":
|
||||
"Go モデルを使用するには、opencode の設定で「OpenCode Go」をプロバイダーとして選択してください。",
|
||||
"workspace.lite.providers.title": "プロバイダー",
|
||||
"workspace.lite.providers.description": "ルーティングに使用するプロバイダーを管理します。",
|
||||
"workspace.lite.providers.useChina": "中国でホストされているモデルを有効にする",
|
||||
"workspace.lite.black.message":
|
||||
"現在 OpenCode Black を購読中、またはウェイティングリストに登録されています。Go に切り替える場合は、先に登録を解除してください。",
|
||||
"workspace.lite.other.message":
|
||||
|
|
|
|||
|
|
@ -646,6 +646,9 @@ export const dict = {
|
|||
"workspace.lite.subscription.useBalance": "사용 한도 도달 후에는 보유 잔액 사용",
|
||||
"workspace.lite.subscription.selectProvider":
|
||||
'Go 모델을 사용하려면 opencode 설정에서 "OpenCode Go"를 공급자로 선택하세요.',
|
||||
"workspace.lite.providers.title": "공급자",
|
||||
"workspace.lite.providers.description": "라우팅에 사용할 공급자를 제어합니다.",
|
||||
"workspace.lite.providers.useChina": "중국에서 호스팅되는 모델 활성화",
|
||||
"workspace.lite.black.message":
|
||||
"현재 OpenCode Black을 구독 중이거나 대기 명단에 등록되어 있습니다. Go로 전환하려면 먼저 구독을 취소해 주세요.",
|
||||
"workspace.lite.other.message":
|
||||
|
|
|
|||
|
|
@ -653,6 +653,9 @@ export const dict = {
|
|||
"workspace.lite.subscription.useBalance": "Bruk din tilgjengelige saldo etter å ha nådd bruksgrensene",
|
||||
"workspace.lite.subscription.selectProvider":
|
||||
'Velg "OpenCode Go" som leverandør i opencode-konfigurasjonen din for å bruke Go-modeller.',
|
||||
"workspace.lite.providers.title": "Leverandører",
|
||||
"workspace.lite.providers.description": "Kontroller hvilke leverandører som brukes til ruting.",
|
||||
"workspace.lite.providers.useChina": "Aktiver modeller hostet i Kina",
|
||||
"workspace.lite.black.message":
|
||||
"Du abonnerer for øyeblikket på OpenCode Black eller står på venteliste. Vennligst avslutt abonnementet først hvis du vil bytte til Go.",
|
||||
"workspace.lite.other.message":
|
||||
|
|
|
|||
|
|
@ -654,6 +654,9 @@ export const dict = {
|
|||
"workspace.lite.subscription.useBalance": "Użyj dostępnego salda po osiągnięciu limitów użycia",
|
||||
"workspace.lite.subscription.selectProvider":
|
||||
'Wybierz "OpenCode Go" jako dostawcę w konfiguracji opencode, aby używać modeli Go.',
|
||||
"workspace.lite.providers.title": "Dostawcy",
|
||||
"workspace.lite.providers.description": "Kontroluj, którzy dostawcy są używani do routingu.",
|
||||
"workspace.lite.providers.useChina": "Włącz modele hostowane w Chinach",
|
||||
"workspace.lite.black.message":
|
||||
"Obecnie subskrybujesz OpenCode Black lub jesteś na liście oczekujących. Jeśli chcesz przejść na Go, najpierw anuluj subskrypcję.",
|
||||
"workspace.lite.other.message":
|
||||
|
|
|
|||
|
|
@ -660,6 +660,9 @@ export const dict = {
|
|||
"workspace.lite.subscription.useBalance": "Использовать доступный баланс после достижения лимитов",
|
||||
"workspace.lite.subscription.selectProvider":
|
||||
'Выберите "OpenCode Go" в качестве провайдера в настройках opencode для использования моделей Go.',
|
||||
"workspace.lite.providers.title": "Провайдеры",
|
||||
"workspace.lite.providers.description": "Управляйте провайдерами, используемыми для маршрутизации.",
|
||||
"workspace.lite.providers.useChina": "Включить модели, размещенные в Китае",
|
||||
"workspace.lite.black.message":
|
||||
"Вы подписаны на OpenCode Black или находитесь в списке ожидания. Пожалуйста, сначала отмените подписку, если хотите перейти на Go.",
|
||||
"workspace.lite.other.message":
|
||||
|
|
|
|||
|
|
@ -649,6 +649,9 @@ export const dict = {
|
|||
"workspace.lite.subscription.useBalance": "ใช้ยอดคงเหลือของคุณหลังจากถึงขีดจำกัดการใช้งาน",
|
||||
"workspace.lite.subscription.selectProvider":
|
||||
'เลือก "OpenCode Go" เป็นผู้ให้บริการในการตั้งค่า opencode ของคุณเพื่อใช้โมเดล Go',
|
||||
"workspace.lite.providers.title": "ผู้ให้บริการ",
|
||||
"workspace.lite.providers.description": "ควบคุมผู้ให้บริการที่ใช้สำหรับการกำหนดเส้นทาง",
|
||||
"workspace.lite.providers.useChina": "เปิดใช้โมเดลที่โฮสต์ในจีน",
|
||||
"workspace.lite.black.message":
|
||||
"ขณะนี้คุณสมัครสมาชิก OpenCode Black หรืออยู่ในรายการรอ โปรดยกเลิกการสมัครก่อนหากต้องการเปลี่ยนไปใช้ Go",
|
||||
"workspace.lite.other.message":
|
||||
|
|
|
|||
|
|
@ -656,6 +656,9 @@ export const dict = {
|
|||
"workspace.lite.subscription.useBalance": "Kullanım limitlerine ulaştıktan sonra mevcut bakiyenizi kullanın",
|
||||
"workspace.lite.subscription.selectProvider":
|
||||
'Go modellerini kullanmak için opencode yapılandırmanızda "OpenCode Go"\'yu sağlayıcı olarak seçin.',
|
||||
"workspace.lite.providers.title": "Sağlayıcılar",
|
||||
"workspace.lite.providers.description": "Yönlendirme için hangi sağlayıcıların kullanılacağını kontrol edin.",
|
||||
"workspace.lite.providers.useChina": "Çin'de barındırılan modelleri etkinleştir",
|
||||
"workspace.lite.black.message":
|
||||
"Şu anda OpenCode Black abonesisiniz veya bekleme listesindesiniz. Go'ya geçmek istiyorsanız lütfen önce aboneliğinizi iptal edin.",
|
||||
"workspace.lite.other.message":
|
||||
|
|
|
|||
|
|
@ -652,6 +652,9 @@ export const dict = {
|
|||
"workspace.lite.subscription.resetsIn": "Скидається через",
|
||||
"workspace.lite.subscription.useBalance": "Використовуйте доступний баланс після досягнення лімітів",
|
||||
"workspace.lite.subscription.selectProvider": 'Виберіть "OpenCode Go" як провайдера в конфігурації opencode.',
|
||||
"workspace.lite.providers.title": "Провайдери",
|
||||
"workspace.lite.providers.description": "Керуйте провайдерами, які використовуються для маршрутизації.",
|
||||
"workspace.lite.providers.useChina": "Увімкнути моделі, розміщені в Китаї",
|
||||
"workspace.lite.black.message":
|
||||
"Ви вже підписані на OpenCode Black або в списку очікування. Спочатку скасуйте підписку, якщо хочете перейти на Go.",
|
||||
"workspace.lite.other.message": "Інший учасник цього робочого простору вже підписаний на OpenCode Go.",
|
||||
|
|
|
|||
|
|
@ -631,6 +631,9 @@ export const dict = {
|
|||
"workspace.lite.subscription.useBalance": "达到使用限额后使用您的可用余额",
|
||||
"workspace.lite.subscription.selectProvider":
|
||||
"在你的 opencode 配置中选择「OpenCode Go」作为提供商,即可使用 Go 模型。",
|
||||
"workspace.lite.providers.title": "提供商",
|
||||
"workspace.lite.providers.description": "控制用于路由的提供商。",
|
||||
"workspace.lite.providers.useChina": "启用部署在中国的模型",
|
||||
"workspace.lite.black.message": "您当前已订阅 OpenCode Black 或在候补名单中。如需切换到 Go,请先取消订阅。",
|
||||
"workspace.lite.other.message": "此工作区中的另一位成员已经订阅了 OpenCode Go。每个工作区只有一名成员可以订阅。",
|
||||
"workspace.lite.promo.description":
|
||||
|
|
|
|||
|
|
@ -631,6 +631,9 @@ export const dict = {
|
|||
"workspace.lite.subscription.useBalance": "達到使用限制後使用您的可用餘額",
|
||||
"workspace.lite.subscription.selectProvider":
|
||||
"在您的 opencode 設定中選擇「OpenCode Go」作為提供商,即可使用 Go 模型。",
|
||||
"workspace.lite.providers.title": "提供商",
|
||||
"workspace.lite.providers.description": "控制用於路由的提供商。",
|
||||
"workspace.lite.providers.useChina": "啟用部署在中國的模型",
|
||||
"workspace.lite.black.message": "您目前已訂閱 OpenCode Black 或在候補名單中。若要切換至 Go,請先取消訂閱。",
|
||||
"workspace.lite.other.message": "此工作區中的另一位成員已訂閱 OpenCode Go。每個工作區只能有一位成員訂閱。",
|
||||
"workspace.lite.promo.description":
|
||||
|
|
|
|||
5
packages/console/app/src/lib/request-country.ts
Normal file
5
packages/console/app/src/lib/request-country.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
export function countryFromRequest(request: Request | undefined) {
|
||||
if (!request) return undefined
|
||||
const cloudflareRequest = request as Request & { cf?: { country?: string } }
|
||||
return cloudflareRequest.cf?.country ?? request.headers.get("cf-ipcountry") ?? undefined
|
||||
}
|
||||
|
|
@ -75,6 +75,40 @@
|
|||
}
|
||||
}
|
||||
|
||||
[data-slot="providers-section"] {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-3);
|
||||
margin-top: var(--space-6);
|
||||
padding-top: var(--space-6);
|
||||
border-top: 1px solid var(--color-border-muted);
|
||||
|
||||
[data-slot="providers-header"] {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-1);
|
||||
|
||||
h3 {
|
||||
color: var(--color-text);
|
||||
font-size: var(--font-size-lg);
|
||||
font-weight: 600;
|
||||
line-height: 1.2;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
p {
|
||||
color: var(--color-text-muted);
|
||||
font-size: var(--font-size-sm);
|
||||
line-height: 1.5;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="setting-row"] {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="toggle-label"] {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,9 @@ import { Modal } from "~/component/modal"
|
|||
import { Billing } from "@opencode-ai/console-core/billing.js"
|
||||
import { Database, eq, and, isNull } from "@opencode-ai/console-core/drizzle/index.js"
|
||||
import { BillingTable, LiteTable } from "@opencode-ai/console-core/schema/billing.sql.js"
|
||||
import { WorkspaceTable } from "@opencode-ai/console-core/schema/workspace.sql.js"
|
||||
import { Actor } from "@opencode-ai/console-core/actor.js"
|
||||
import { Workspace } from "@opencode-ai/console-core/workspace.js"
|
||||
import { Subscription } from "@opencode-ai/console-core/subscription.js"
|
||||
import { LiteData } from "@opencode-ai/console-core/lite.js"
|
||||
import { withActor } from "~/context/auth.withActor"
|
||||
|
|
@ -16,6 +18,8 @@ import { useLanguage } from "~/context/language"
|
|||
import { formError } from "~/lib/form-error"
|
||||
import { formatResetTime, liteResetTimeKeys } from "~/lib/format-reset-time"
|
||||
import { createReferralFromCookie } from "~/lib/referral-invite"
|
||||
import { getRequestEvent } from "solid-js/web"
|
||||
import { countryFromRequest } from "~/lib/request-country"
|
||||
|
||||
import { IconAlipay, IconUpi } from "~/component/icon"
|
||||
|
||||
|
|
@ -34,9 +38,11 @@ export const queryLiteSubscription = query(async (workspaceID: string) => {
|
|||
timeMonthlyUpdated: LiteTable.timeMonthlyUpdated,
|
||||
timeCreated: LiteTable.timeCreated,
|
||||
lite: BillingTable.lite,
|
||||
region: WorkspaceTable.region,
|
||||
})
|
||||
.from(BillingTable)
|
||||
.innerJoin(LiteTable, eq(LiteTable.workspaceID, BillingTable.workspaceID))
|
||||
.innerJoin(WorkspaceTable, eq(WorkspaceTable.id, BillingTable.workspaceID))
|
||||
.where(and(eq(LiteTable.workspaceID, Actor.workspace()), isNull(LiteTable.timeDeleted)))
|
||||
.then((r) => r[0]),
|
||||
)
|
||||
|
|
@ -48,6 +54,7 @@ export const queryLiteSubscription = query(async (workspaceID: string) => {
|
|||
return {
|
||||
mine,
|
||||
useBalance: row.lite?.useBalance ?? false,
|
||||
region: row.region ?? (await Workspace.setDefaultRegion({ country: countryFromRequest(getRequestEvent()?.request) })),
|
||||
rollingUsage: Subscription.analyzeRollingUsage({
|
||||
limit: limits.rollingLimit,
|
||||
window: limits.rollingWindow,
|
||||
|
|
@ -128,6 +135,24 @@ const setLiteUseBalance = action(async (form: FormData) => {
|
|||
)
|
||||
}, "setLiteUseBalance")
|
||||
|
||||
const setGoProviderRouting = action(async (form: FormData) => {
|
||||
"use server"
|
||||
const workspaceID = form.get("workspaceID") as string | null
|
||||
if (!workspaceID) return { error: formError.workspaceRequired }
|
||||
const useChinaProviders = (form.get("useChinaProviders") as string | null) === "true"
|
||||
|
||||
return json(
|
||||
await withActor(
|
||||
() =>
|
||||
Workspace.update({ region: useChinaProviders ? ["us", "eu", "sg"] : ["us", "eu", "sg", "cn"] })
|
||||
.then(() => ({ error: undefined }))
|
||||
.catch((e) => ({ error: e.message as string })),
|
||||
workspaceID,
|
||||
),
|
||||
{ revalidate: queryLiteSubscription.key },
|
||||
)
|
||||
}, "go.providerRouting.set")
|
||||
|
||||
function LiteUsageItem(props: { label: string; usage: { usagePercent: number; resetInSec: number } }) {
|
||||
const i18n = useI18n()
|
||||
|
||||
|
|
@ -159,6 +184,7 @@ export function LiteSection(props: { lite: LiteSubscription | undefined }) {
|
|||
const checkoutAction = useAction(createLiteCheckoutUrl)
|
||||
const checkoutSubmission = useSubmission(createLiteCheckoutUrl)
|
||||
const useBalanceSubmission = useSubmission(setLiteUseBalance)
|
||||
const providerRoutingSubmission = useSubmission(setGoProviderRouting)
|
||||
const [store, setStore] = createStore({
|
||||
loading: undefined as undefined | "session" | "checkout" | "alipay" | "upi",
|
||||
showModal: false,
|
||||
|
|
@ -232,6 +258,28 @@ export function LiteSection(props: { lite: LiteSubscription | undefined }) {
|
|||
<span></span>
|
||||
</label>
|
||||
</form>
|
||||
{/*
|
||||
<div data-slot="providers-section">
|
||||
<div data-slot="providers-header">
|
||||
<h3>{i18n.t("workspace.lite.providers.title")}</h3>
|
||||
<p>{i18n.t("workspace.lite.providers.description")}</p>
|
||||
</div>
|
||||
<form action={setGoProviderRouting} method="post" data-slot="setting-row">
|
||||
<p>{i18n.t("workspace.lite.providers.useChina")}</p>
|
||||
<input type="hidden" name="workspaceID" value={params.id} />
|
||||
<input type="hidden" name="useChinaProviders" value={sub().region.includes("cn") ? "true" : "false"} />
|
||||
<label data-slot="toggle-label">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={sub().region.includes("cn")}
|
||||
disabled={providerRoutingSubmission.pending}
|
||||
onChange={(e) => e.currentTarget.form?.requestSubmit()}
|
||||
/>
|
||||
<span></span>
|
||||
</label>
|
||||
</form>
|
||||
</div>
|
||||
*/}
|
||||
</section>
|
||||
)}
|
||||
</Show>
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ const updateWorkspace = action(async (form: FormData) => {
|
|||
.catch((e) => ({ error: e.message as string })),
|
||||
workspaceID,
|
||||
),
|
||||
{ revalidate: getWorkspaceInfo.key },
|
||||
)
|
||||
}, "workspace.update")
|
||||
|
||||
|
|
|
|||
|
|
@ -49,6 +49,8 @@ import { createModelTpmLimiter } from "./modelTpmLimiter"
|
|||
import { createModelTpsLimiter } from "./modelTpsLimiter"
|
||||
import { createProviderBudgetTracker } from "./providerBudgetTracker"
|
||||
import { accumulateUsage, HOT_WORKSPACES } from "./usageBatcher"
|
||||
import { Workspace } from "@opencode-ai/console-core/workspace.js"
|
||||
import { countryFromRequest } from "~/lib/request-country"
|
||||
|
||||
type ZenData = Awaited<ReturnType<typeof ZenData.list>>
|
||||
type RetryOptions = {
|
||||
|
|
@ -125,6 +127,11 @@ export async function handler(
|
|||
: createKeyRateLimiter(modelInfo.id, modelInfo.rateLimit, zenApiKey, input.request)
|
||||
await rateLimiter?.check()
|
||||
const authInfo = await authenticate(modelInfo, zenApiKey)
|
||||
if (authInfo && !authInfo.region) {
|
||||
await Actor.provide("system", { workspaceID: authInfo.workspaceID }, () =>
|
||||
Workspace.setDefaultRegion({ country: countryFromRequest(input.request) }),
|
||||
)
|
||||
}
|
||||
const stickyId = sessionId ? sessionId : (authInfo?.workspaceID ?? ip)
|
||||
const stickyTracker = createStickyTracker(modelInfo.id, modelInfo.stickyProvider, stickyId)
|
||||
const stickyProvider = await stickyTracker?.get()
|
||||
|
|
@ -657,7 +664,10 @@ export async function handler(
|
|||
tx
|
||||
.select({
|
||||
apiKey: KeyTable.id,
|
||||
workspaceID: KeyTable.workspaceID,
|
||||
workspace: {
|
||||
id: WorkspaceTable.id,
|
||||
region: WorkspaceTable.region,
|
||||
},
|
||||
billing: {
|
||||
balance: BillingTable.balance,
|
||||
paymentMethodID: BillingTable.paymentMethodID,
|
||||
|
|
@ -735,13 +745,13 @@ export async function handler(
|
|||
if (
|
||||
modelInfo.id.startsWith("alpha-") &&
|
||||
Resource.App.stage === "production" &&
|
||||
!ADMIN_WORKSPACES.includes(data.workspaceID)
|
||||
!ADMIN_WORKSPACES.includes(data.workspace.id)
|
||||
)
|
||||
throw new AuthError(t("zen.api.error.modelNotSupported", { model: modelInfo.id }))
|
||||
|
||||
logger.metric({
|
||||
api_key: data.apiKey,
|
||||
workspace: data.workspaceID,
|
||||
workspace: data.workspace.id,
|
||||
user_id: data.user.id,
|
||||
...(() => {
|
||||
if (data.billing.subscription)
|
||||
|
|
@ -758,13 +768,14 @@ export async function handler(
|
|||
|
||||
return {
|
||||
apiKeyId: data.apiKey,
|
||||
workspaceID: data.workspaceID,
|
||||
workspaceID: data.workspace.id,
|
||||
region: data.workspace.region,
|
||||
billing: data.billing,
|
||||
user: data.user,
|
||||
black: data.black,
|
||||
lite: data.lite,
|
||||
provider: data.provider,
|
||||
isFree: ADMIN_WORKSPACES.includes(data.workspaceID),
|
||||
isFree: ADMIN_WORKSPACES.includes(data.workspace.id),
|
||||
isDisabled: !!data.timeDisabled,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
ALTER TABLE `workspace` ADD `region` json;
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,4 +1,4 @@
|
|||
import { primaryKey, mysqlTable, uniqueIndex, varchar } from "drizzle-orm/mysql-core"
|
||||
import { json, primaryKey, mysqlTable, uniqueIndex, varchar } from "drizzle-orm/mysql-core"
|
||||
import { timestamps, ulid } from "../drizzle/types"
|
||||
|
||||
export const WorkspaceTable = mysqlTable(
|
||||
|
|
@ -7,6 +7,7 @@ export const WorkspaceTable = mysqlTable(
|
|||
id: ulid("id").notNull().primaryKey(),
|
||||
slug: varchar("slug", { length: 255 }),
|
||||
name: varchar("name", { length: 255 }).notNull(),
|
||||
region: json("region").$type<("us" | "eu" | "sg" | "cn")[]>(),
|
||||
...timestamps,
|
||||
},
|
||||
(table) => [uniqueIndex("slug").on(table.slug)],
|
||||
|
|
|
|||
|
|
@ -11,6 +11,9 @@ import { Key } from "./key"
|
|||
import { and, eq, isNull, sql } from "drizzle-orm"
|
||||
|
||||
export namespace Workspace {
|
||||
export const Region = z.enum(["us", "eu", "sg", "cn"])
|
||||
export type Region = z.infer<typeof Region>
|
||||
|
||||
export const create = fn(
|
||||
z.object({
|
||||
name: z.string().min(1),
|
||||
|
|
@ -57,22 +60,41 @@ export namespace Workspace {
|
|||
|
||||
export const update = fn(
|
||||
z.object({
|
||||
name: z.string().min(1).max(255),
|
||||
name: z.string().min(1).max(255).optional(),
|
||||
region: z.array(Region).min(1).optional(),
|
||||
}),
|
||||
async ({ name }) => {
|
||||
async (input) => {
|
||||
Actor.assertAdmin()
|
||||
const workspaceID = Actor.workspace()
|
||||
return await Database.use((tx) =>
|
||||
tx
|
||||
.update(WorkspaceTable)
|
||||
.set({
|
||||
name,
|
||||
...("name" in input ? { name: input.name } : {}),
|
||||
...("region" in input ? { region: input.region } : {}),
|
||||
})
|
||||
.where(eq(WorkspaceTable.id, workspaceID)),
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
export const setDefaultRegion = fn(
|
||||
z.object({
|
||||
country: z.string().optional(),
|
||||
}),
|
||||
async (input) => {
|
||||
const region: Workspace.Region[] =
|
||||
input.country?.toUpperCase() === "CN" ? ["us", "eu", "sg", "cn"] : ["us", "eu", "sg"]
|
||||
await Database.use((tx) =>
|
||||
tx
|
||||
.update(WorkspaceTable)
|
||||
.set({ region })
|
||||
.where(and(eq(WorkspaceTable.id, Actor.workspace()), isNull(WorkspaceTable.region))),
|
||||
)
|
||||
return region
|
||||
},
|
||||
)
|
||||
|
||||
export const remove = fn(z.void(), async () => {
|
||||
await Database.use((tx) =>
|
||||
tx
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue