core: keep Go referral links crediting new signups

This commit is contained in:
vimtor
2026-05-19 11:08:59 +02:00
parent 769b982803
commit 12b67b4ba9
6 changed files with 30 additions and 29 deletions

View File

@@ -81,7 +81,7 @@ function CopyInviteLink(props: { summary: GoReferralSummary }) {
? window.location.origin
: undefined
const inviteUrl = createMemo(() => {
const path = `/go?ref=${props.summary.inviteCode}`
const path = `/go?ref=${props.summary.referralCode}`
if (!origin) return path
return new URL(path, origin).toString()
})

View File

@@ -1,28 +1,28 @@
import { Referral } from "@opencode-ai/console-core/referral.js"
const INVITE_COOKIE = "oc_referral"
const INVITE_MAX_AGE = 60 * 60 * 24 * 30
const REFERRAL_COOKIE = "oc_referral"
const REFERRAL_MAX_AGE = 60 * 60 * 24 * 30
export function normalizeInviteCode(code?: string | null) {
export function normalizeReferralCode(code?: string | null) {
return Referral.normalizeCode(code)
}
export function inviteCookie(code: string) {
return `${INVITE_COOKIE}=${encodeURIComponent(code)}; Path=/; Max-Age=${INVITE_MAX_AGE}; SameSite=Lax; HttpOnly`
export function referralCookie(code: string) {
return `${REFERRAL_COOKIE}=${encodeURIComponent(code)}; Path=/; Max-Age=${REFERRAL_MAX_AGE}; SameSite=Lax; HttpOnly`
}
export function clearInviteCookie() {
return `${INVITE_COOKIE}=; Path=/; Max-Age=0; SameSite=Lax; HttpOnly`
export function clearReferralCookie() {
return `${REFERRAL_COOKIE}=; Path=/; Max-Age=0; SameSite=Lax; HttpOnly`
}
export function inviteFromCookieHeader(header: string | null) {
export function referralCodeFromCookieHeader(header: string | null) {
if (!header) return undefined
return normalizeInviteCode(
return normalizeReferralCode(
header
.split(";")
.map((x) => x.trim())
.find((x) => x.startsWith(`${INVITE_COOKIE}=`))
?.slice(`${INVITE_COOKIE}=`.length),
.find((x) => x.startsWith(`${REFERRAL_COOKIE}=`))
?.slice(`${REFERRAL_COOKIE}=`.length),
)
}

View File

@@ -1,6 +1,6 @@
import { createMiddleware } from "@solidjs/start/middleware"
import { LOCALE_HEADER, cookie, fromPathname, strip } from "~/lib/language"
import { inviteCookie, normalizeInviteCode } from "~/lib/referral-invite"
import { normalizeReferralCode, referralCookie } from "~/lib/referral-invite"
export default createMiddleware({
onRequest(event) {
@@ -14,7 +14,7 @@ export default createMiddleware({
event.response.headers.append("set-cookie", cookie(locale))
}
const inviteCode = normalizeInviteCode(url.searchParams.get("ref"))
if (inviteCode) event.response.headers.append("set-cookie", inviteCookie(inviteCode))
const referralCode = normalizeReferralCode(url.searchParams.get("ref"))
if (referralCode) event.response.headers.append("set-cookie", referralCookie(referralCode))
},
})

View File

@@ -5,7 +5,7 @@ import { AuthClient } from "~/context/auth"
import { useAuthSession } from "~/context/auth"
import { i18n } from "~/i18n"
import { localeFromRequest, route } from "~/lib/language"
import { clearInviteCookie, inviteFromCookieHeader } from "~/lib/referral-invite"
import { clearReferralCookie, referralCodeFromCookieHeader } from "~/lib/referral-invite"
export async function GET(input: APIEvent) {
const url = new URL(input.request.url)
@@ -19,7 +19,7 @@ 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 inviteCode = inviteFromCookieHeader(input.request.headers.get("cookie"))
const referralCode = referralCodeFromCookieHeader(input.request.headers.get("cookie"))
const session = await useAuthSession()
const id = decoded.subject.properties.accountID
await session.update((value) => {
@@ -35,14 +35,14 @@ export async function GET(input: APIEvent) {
current: id,
}
})
if (decoded.subject.properties.newAccount && inviteCode) {
await Referral.createFromAccount({ accountID: id, inviteCode }).catch((error) => {
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 (inviteCode) response.headers.append("set-cookie", clearInviteCookie())
if (referralCode) response.headers.append("set-cookie", clearReferralCookie())
return response
} catch (e: any) {
return new Response(

View File

@@ -226,11 +226,12 @@ function LimitsGraph(props: { href: string }) {
export default function Home() {
const location = useLocation()
const workspaceID = createAsync(() => checkLoggedIn())
const inviteCode = createMemo(() => new URLSearchParams(location.search).get("ref") ?? undefined)
const referralCode = createMemo(() => new URLSearchParams(location.search).get("ref") ?? undefined)
const subscribeUrl = createMemo(() => {
const invite = inviteCode() ? `?ref=${encodeURIComponent(inviteCode()!)}` : ""
if (workspaceID()) return `/workspace/${workspaceID()}/go${invite}`
return `/auth${invite}`
const code = referralCode()
const referral = code ? `?ref=${encodeURIComponent(code)}` : ""
if (workspaceID()) return `/workspace/${workspaceID()}/go${referral}`
return `/auth${referral}`
})
const i18n = useI18n()
const language = useLanguage()

View File

@@ -153,7 +153,7 @@ export namespace Referral {
(a, b) => new Date(b.timeCreated).getTime() - new Date(a.timeCreated).getTime(),
)
return {
inviteCode: code.code,
referralCode: code.code,
inviteCount: allRewards.length,
hasActiveGo: !!rows.lite,
rewardAmount: microCentsToCents(REWARD_AMOUNT),
@@ -279,16 +279,16 @@ export namespace Referral {
export async function createFromAccount(input: {
accountID: string
inviteCode?: string
referralCode?: string
}) {
const inviteCode = normalizeCode(input.inviteCode)
if (!inviteCode) return { status: "missing-code" as const }
const referralCode = normalizeCode(input.referralCode)
if (!referralCode) return { status: "missing-code" as const }
return Database.transaction(async (tx) => {
const code = await tx
.select({ workspaceID: WorkspaceTable.id })
.from(WorkspaceTable)
.where(and(eq(WorkspaceTable.referralCode, inviteCode), isNull(WorkspaceTable.timeDeleted)))
.where(and(eq(WorkspaceTable.referralCode, referralCode), isNull(WorkspaceTable.timeDeleted)))
.then((rows) => rows[0])
if (!code) return { status: "invalid-code" as const }