Victor Navarro 2 месяцев назад
Родитель
Сommit
fdc574ff81

+ 18 - 1
packages/console/app/src/component/go-referral.tsx

@@ -2,6 +2,7 @@ import { action, json, query, useAction, useSubmission } from "@solidjs/router"
 import { createEffect, createMemo, createSignal, For, onCleanup, Show } from "solid-js"
 import { getRequestEvent } from "solid-js/web"
 import { Referral } from "@opencode-ai/console-core/referral.js"
+import { Actor } from "@opencode-ai/console-core/actor.js"
 import { withActor } from "~/context/auth.withActor"
 import { Modal } from "~/component/modal"
 import { IconCheck, IconCopy } from "~/component/icon"
@@ -9,6 +10,7 @@ import { useI18n } from "~/context/i18n"
 import { useLanguage } from "~/context/language"
 import { formatResetTime, liteResetTimeKeys } from "~/lib/format-reset-time"
 import { queryLiteSubscription } from "~/routes/workspace/[id]/go/lite-section"
+import { clearReferralCookie, referralCodeFromCookieHeader } from "~/lib/referral-invite"
 import "./go-referral.css"
 
 type GoReferralSummary = Awaited<ReturnType<typeof Referral.summary>>
@@ -25,7 +27,21 @@ const emptyUsagePreview = {
 
 export const queryGoReferral = query(async (workspaceID: string) => {
   "use server"
-  return withActor(() => Referral.summary(), workspaceID)
+  return withActor(async () => {
+    const event = getRequestEvent()
+    const referralCode = referralCodeFromCookieHeader(event?.request.headers.get("cookie") ?? null)
+    if (referralCode) {
+      await Referral.createFromAccount({
+        accountID: Actor.account(),
+        referralCode,
+      }).catch((error) => {
+        console.error("Referral create failed", error)
+      })
+      event?.response.headers.append("set-cookie", clearReferralCookie())
+    }
+
+    return Referral.summary()
+  }, workspaceID)
 }, "go.referral.get")
 
 export const queryGoReferralUsagePreview = query(async (workspaceID: string, referralID?: string) => {
@@ -65,6 +81,7 @@ function rewardDescriptionKey(source: GoReferralReward["source"]) {
 
 function rewardActionKey(reward: GoReferralReward, hasActiveGo: boolean) {
   if (reward.status === "applied") return "workspace.referral.reward.action.applied" as const
+  if (reward.status === "pending" && reward.source === "inviter") return "workspace.referral.reward.source.pendingInviter" as const
   if (reward.status === "pending" || !hasActiveGo) return "workspace.referral.reward.action.subscribeUnlock" as const
   return "workspace.referral.reward.action.view" as const
 }

+ 1 - 11
packages/console/app/src/routes/auth/[...callback].ts

@@ -1,11 +1,9 @@
 import { redirect } from "@solidjs/router"
 import type { APIEvent } from "@solidjs/start/server"
-import { Referral } from "@opencode-ai/console-core/referral.js"
 import { AuthClient } from "~/context/auth"
 import { useAuthSession } from "~/context/auth"
 import { i18n } from "~/i18n"
 import { localeFromRequest, route } from "~/lib/language"
-import { clearReferralCookie, referralCodeFromCookieHeader } from "~/lib/referral-invite"
 
 export async function GET(input: APIEvent) {
   const url = new URL(input.request.url)
@@ -19,7 +17,6 @@ export async function GET(input: APIEvent) {
     if (result.err) throw new Error(result.err.message)
     const decoded = AuthClient.decode(result.tokens.access, {} as any)
     if (decoded.err) throw new Error(decoded.err.message)
-    const referralCode = referralCodeFromCookieHeader(input.request.headers.get("cookie"))
     const session = await useAuthSession()
     const id = decoded.subject.properties.accountID
     await session.update((value) => {
@@ -35,15 +32,8 @@ export async function GET(input: APIEvent) {
         current: id,
       }
     })
-    if (decoded.subject.properties.newAccount && referralCode) {
-      await Referral.createFromAccount({ accountID: id, referralCode }).catch((error) => {
-        console.error("Referral create failed", error)
-      })
-    }
     const next = url.pathname === "/auth/callback" ? "/auth" : url.pathname.replace("/auth/callback", "")
-    const response = redirect(route(locale, next))
-    if (referralCode) response.headers.append("set-cookie", clearReferralCookie())
-    return response
+    return redirect(route(locale, next))
   } catch (e: any) {
     return new Response(
       JSON.stringify({

+ 1 - 0
packages/console/app/vite.config.ts

@@ -17,6 +17,7 @@ export default defineConfig({
   ],
   server: {
     allowedHosts: true,
+    port: 3001,
   },
   build: {
     rollupOptions: {

+ 24 - 4
packages/console/core/src/referral.ts

@@ -1,8 +1,8 @@
 import { z } from "zod"
-import { and, asc, eq, isNull, sql, Database } from "./drizzle"
+import { and, asc, eq, inArray, isNull, sql, Database } from "./drizzle"
 import { Actor } from "./actor"
 import { Identifier } from "./identifier"
-import { LiteTable } from "./schema/billing.sql"
+import { LiteTable, PaymentTable } from "./schema/billing.sql"
 import { ReferralCodeTable, ReferralRewardTable, ReferralTable } from "./schema/referral.sql"
 import { AuthTable } from "./schema/auth.sql"
 import { UserTable } from "./schema/user.sql"
@@ -318,6 +318,26 @@ export namespace Referral {
         .then((rows) => rows[0])
       if (selfReferral) throw new Error("Self-referral is not allowed")
 
+      const workspaceIDs = await tx
+        .select({ workspaceID: UserTable.workspaceID })
+        .from(UserTable)
+        .where(and(eq(UserTable.accountID, input.accountID), isNull(UserTable.timeDeleted)))
+        .then((rows) => rows.map((row) => row.workspaceID))
+      if (workspaceIDs.length === 0) return
+
+      const litePayment = await tx
+        .select({ id: PaymentTable.id })
+        .from(PaymentTable)
+        .where(
+          and(
+            inArray(PaymentTable.workspaceID, workspaceIDs),
+            isNull(PaymentTable.timeDeleted),
+            sql`JSON_UNQUOTE(JSON_EXTRACT(${PaymentTable.enrichment}, '$.type')) = 'lite'`,
+          ),
+        )
+        .then((rows) => rows[0])
+      if (litePayment) return
+
       const referralID = Identifier.create("referral")
       await tx.insert(ReferralTable).ignore().values({
         workspaceID: code.workspaceID,
@@ -355,7 +375,7 @@ export namespace Referral {
         .from(ReferralTable)
         .where(and(eq(ReferralTable.inviteeAccountID, invitee.accountID), isNull(ReferralTable.timeDeleted)))
         .then((rows) => rows[0])
-      if (!referral) throw new Error("Referral not found")
+      if (!referral) return
 
       const result = await tx
         .insert(ReferralRewardTable)
@@ -373,7 +393,7 @@ export namespace Referral {
           },
         ])
 
-      if (result.rowsAffected === 0) throw new Error("Referral already completed")
+      if (result.rowsAffected === 0) return
     })
   }
 

+ 1 - 0
packages/enterprise/vite.config.ts

@@ -29,6 +29,7 @@ export default defineConfig({
   server: {
     host: "0.0.0.0",
     allowedHosts: true,
+    port: 3002,
   },
   worker: {
     format: "es",

+ 2 - 1
packages/stats/app/tsconfig.json

@@ -17,5 +17,6 @@
     "paths": {
       "~/*": ["./src/*"]
     }
-  }
+  },
+  "include": ["*.ts", "src", "../core/src/resource.d.ts"]
 }

+ 8 - 48
sst-env.d.ts

@@ -26,6 +26,14 @@ declare module "sst" {
     "AuthApi": import("@cloudflare/workers-types").Service
     "AuthStorage": import("@cloudflare/workers-types").KVNamespace
     "Bucket": import("@cloudflare/workers-types").R2Bucket
+    "CLOUDFLARE_API_TOKEN": {
+      "type": "sst.sst.Secret"
+      "value": string
+    }
+    "CLOUDFLARE_DEFAULT_ACCOUNT_ID": {
+      "type": "sst.sst.Secret"
+      "value": string
+    }
     "Console": {
       "type": "sst.cloudflare.SolidStart"
       "url": string
@@ -91,37 +99,6 @@ declare module "sst" {
       "type": "random.index/randomPassword.RandomPassword"
       "value": string
     }
-    "InferenceEvent": {
-      "catalog": string
-      "database": string
-      "region": string
-      "table": string
-      "tableBucket": string
-      "type": "sst.sst.Linkable"
-      "workgroup": string
-    }
-    "LakeIngest": {
-      "secret": string
-      "type": "sst.sst.Linkable"
-      "url": string
-    }
-    "LakeIngestConfig": {
-      "secret": string
-      "streamName": string
-      "type": "sst.sst.Linkable"
-    }
-    "LakeIngestSecret": {
-      "type": "random.index/randomPassword.RandomPassword"
-      "value": string
-    }
-    "LakeIngestService": {
-      "service": string
-      "type": "sst.aws.Service"
-      "url": string
-    }
-    "LakeVpc": {
-      "type": "sst.aws.Vpc"
-    }
     "LogProcessor": import("@cloudflare/workers-types").Service
     "R2AccessKey": {
       "type": "sst.sst.Secret"
@@ -156,23 +133,6 @@ declare module "sst" {
       "value": string
     }
     "Stat": import("@cloudflare/workers-types").Service
-    "StatsDatabase": {
-      "database": string
-      "host": string
-      "password": string
-      "port": number
-      "type": "sst.sst.Linkable"
-      "url": string
-      "username": string
-    }
-    "StatsSyncConfig": {
-      "dataset": string
-      "type": "sst.sst.Linkable"
-    }
-    "StatsSyncService": {
-      "service": string
-      "type": "sst.aws.Service"
-    }
     "Teams": {
       "type": "sst.cloudflare.SolidStart"
       "url": string