opencode/infra/console.ts

308 lines
9.1 KiB
TypeScript
Raw Permalink Normal View History

import { deployAws, domain } from "./stage"
2025-09-30 02:17:53 +08:00
import { EMAILOCTOPUS_API_KEY } from "./app"
import { SECRET } from "./secret"
2025-08-09 01:22:54 +08:00
const lake = deployAws ? await import("./lake") : undefined
2025-08-10 13:17:48 +08:00
////////////////
// DATABASE
////////////////
2025-08-09 01:22:54 +08:00
2025-09-02 15:14:56 +08:00
const cluster = planetscale.getDatabaseOutput({
name: "opencode",
2025-09-09 03:46:57 +08:00
organization: "anomalyco",
2025-09-02 15:14:56 +08:00
})
const branch =
$app.stage === "production"
? planetscale.getBranchOutput({
name: "production",
organization: cluster.organization,
database: cluster.name,
})
: new planetscale.Branch("DatabaseBranch", {
database: cluster.name,
organization: cluster.organization,
name: $app.stage,
parentBranch: "production",
})
const password = new planetscale.Password("DatabasePassword", {
name: $app.stage,
database: cluster.name,
organization: cluster.organization,
branch: branch.name,
})
2025-08-09 01:22:54 +08:00
export const database = new sst.Linkable("Database", {
properties: {
2025-09-02 15:14:56 +08:00
host: password.accessHostUrl,
database: cluster.name,
username: password.username,
password: password.plaintext,
port: 3306,
2025-08-09 01:22:54 +08:00
},
})
new sst.x.DevCommand("Studio", {
link: [database],
dev: {
command: "bun db studio",
2025-09-18 22:59:01 +08:00
directory: "packages/console/core",
2025-08-09 01:22:54 +08:00
autostart: true,
},
})
2025-08-10 13:17:48 +08:00
////////////////
// AUTH
////////////////
2025-08-09 01:22:54 +08:00
const GITHUB_CLIENT_ID_CONSOLE = new sst.Secret("GITHUB_CLIENT_ID_CONSOLE")
const GITHUB_CLIENT_SECRET_CONSOLE = new sst.Secret("GITHUB_CLIENT_SECRET_CONSOLE")
2025-08-09 13:28:27 +08:00
const GOOGLE_CLIENT_ID = new sst.Secret("GOOGLE_CLIENT_ID")
2025-08-09 01:22:54 +08:00
const authStorage = new sst.cloudflare.Kv("AuthStorage")
export const auth = new sst.cloudflare.Worker("AuthApi", {
domain: `auth.${domain}`,
2025-09-18 22:59:01 +08:00
handler: "packages/console/function/src/auth.ts",
2025-08-09 01:22:54 +08:00
url: true,
2025-11-08 09:59:02 +08:00
link: [database, authStorage, GITHUB_CLIENT_ID_CONSOLE, GITHUB_CLIENT_SECRET_CONSOLE, GOOGLE_CLIENT_ID],
2025-08-09 01:22:54 +08:00
})
2025-08-10 13:17:48 +08:00
////////////////
// GATEWAY
////////////////
2025-09-26 19:54:30 +08:00
export const stripeWebhook = new stripe.WebhookEndpoint("StripeWebhookEndpoint", {
2025-09-03 23:40:59 +08:00
url: $interpolate`https://${domain}/stripe/webhook`,
2025-08-10 13:17:48 +08:00
enabledEvents: [
"checkout.session.async_payment_failed",
"checkout.session.async_payment_succeeded",
"checkout.session.completed",
"checkout.session.expired",
2025-09-24 05:58:26 +08:00
"charge.refunded",
2026-01-07 14:55:36 +08:00
"invoice.payment_succeeded",
2026-01-24 21:33:47 +08:00
"invoice.payment_failed",
"invoice.payment_action_required",
2025-08-10 13:17:48 +08:00
"customer.created",
"customer.deleted",
"customer.updated",
"customer.discount.created",
"customer.discount.deleted",
"customer.discount.updated",
"customer.source.created",
"customer.source.deleted",
"customer.source.expiring",
"customer.source.updated",
"customer.subscription.created",
"customer.subscription.deleted",
"customer.subscription.paused",
"customer.subscription.pending_update_applied",
"customer.subscription.pending_update_expired",
"customer.subscription.resumed",
"customer.subscription.trial_will_end",
"customer.subscription.updated",
],
})
2026-02-23 07:41:34 +08:00
const zenLiteProduct = new stripe.Product("ZenLite", {
2026-02-26 01:39:48 +08:00
name: "OpenCode Go",
2026-02-23 07:41:34 +08:00
})
2026-03-11 22:46:16 +08:00
const zenLiteCouponFirstMonth50 = new stripe.Coupon("ZenLiteCouponFirstMonth50", {
name: "First month 50% off",
percentOff: 50,
appliesToProducts: [zenLiteProduct.id],
duration: "once",
})
2026-04-07 22:08:57 +08:00
const zenLiteCouponFirstMonth100 = new stripe.Coupon("ZenLiteCouponFirstMonth100", {
name: "First month 100% off",
percentOff: 100,
appliesToProducts: [zenLiteProduct.id],
duration: "once",
})
2026-04-27 13:36:26 +08:00
const zenLiteCouponThreeMonths100 = new stripe.Coupon("ZenLiteCoupon3Months100", {
name: "3 months 100% off",
percentOff: 100,
appliesToProducts: [zenLiteProduct.id],
duration: "repeating",
durationInMonths: 3,
})
const zenLiteCouponSixMonths100 = new stripe.Coupon("ZenLiteCoupon6Months100", {
name: "6 months 100% off",
percentOff: 100,
appliesToProducts: [zenLiteProduct.id],
duration: "repeating",
durationInMonths: 6,
})
const zenLiteCouponTwelveMonths100 = new stripe.Coupon("ZenLiteCoupon12Months100", {
name: "12 months 100% off",
percentOff: 100,
appliesToProducts: [zenLiteProduct.id],
duration: "repeating",
durationInMonths: 12,
})
2026-02-23 07:41:34 +08:00
const zenLitePrice = new stripe.Price("ZenLitePrice", {
product: zenLiteProduct.id,
currency: "usd",
recurring: {
interval: "month",
intervalCount: 1,
},
unitAmount: 1000,
})
const ZEN_LITE_PRICE = new sst.Linkable("ZEN_LITE_PRICE", {
properties: {
product: zenLiteProduct.id,
price: zenLitePrice.id,
2026-03-20 06:44:21 +08:00
priceInr: 92900,
2026-03-11 22:46:16 +08:00
firstMonth50Coupon: zenLiteCouponFirstMonth50.id,
2026-04-07 22:08:57 +08:00
firstMonth100Coupon: zenLiteCouponFirstMonth100.id,
2026-04-27 13:36:26 +08:00
threeMonths100Coupon: zenLiteCouponThreeMonths100.id,
sixMonths100Coupon: zenLiteCouponSixMonths100.id,
twelveMonths100Coupon: zenLiteCouponTwelveMonths100.id,
2026-02-23 07:41:34 +08:00
},
})
const zenBlackProduct = new stripe.Product("ZenBlack", {
2026-01-07 03:22:52 +08:00
name: "OpenCode Black",
})
2026-02-23 07:41:34 +08:00
const zenBlackPriceProps = {
product: zenBlackProduct.id,
2026-01-07 03:22:52 +08:00
currency: "usd",
recurring: {
interval: "month",
intervalCount: 1,
},
2026-01-23 01:40:42 +08:00
}
2026-02-23 07:41:34 +08:00
const zenBlackPrice200 = new stripe.Price("ZenBlackPrice", { ...zenBlackPriceProps, unitAmount: 20000 })
const zenBlackPrice100 = new stripe.Price("ZenBlack100Price", { ...zenBlackPriceProps, unitAmount: 10000 })
const zenBlackPrice20 = new stripe.Price("ZenBlack20Price", { ...zenBlackPriceProps, unitAmount: 2000 })
2026-01-23 01:40:42 +08:00
const ZEN_BLACK_PRICE = new sst.Linkable("ZEN_BLACK_PRICE", {
properties: {
2026-02-23 07:41:34 +08:00
product: zenBlackProduct.id,
plan200: zenBlackPrice200.id,
plan100: zenBlackPrice100.id,
plan20: zenBlackPrice20.id,
2026-01-23 01:40:42 +08:00
},
2026-01-07 03:22:52 +08:00
})
2025-11-22 01:50:51 +08:00
const ZEN_MODELS = [
new sst.Secret("ZEN_MODELS1"),
new sst.Secret("ZEN_MODELS2"),
new sst.Secret("ZEN_MODELS3"),
new sst.Secret("ZEN_MODELS4"),
2025-12-12 12:41:04 +08:00
new sst.Secret("ZEN_MODELS5"),
2025-12-24 09:36:52 +08:00
new sst.Secret("ZEN_MODELS6"),
2026-01-04 13:58:06 +08:00
new sst.Secret("ZEN_MODELS7"),
2026-01-16 07:21:01 +08:00
new sst.Secret("ZEN_MODELS8"),
2026-01-31 01:19:36 +08:00
new sst.Secret("ZEN_MODELS9"),
new sst.Secret("ZEN_MODELS10"),
2026-02-08 23:31:07 +08:00
new sst.Secret("ZEN_MODELS11"),
new sst.Secret("ZEN_MODELS12"),
new sst.Secret("ZEN_MODELS13"),
new sst.Secret("ZEN_MODELS14"),
new sst.Secret("ZEN_MODELS15"),
new sst.Secret("ZEN_MODELS16"),
new sst.Secret("ZEN_MODELS17"),
new sst.Secret("ZEN_MODELS18"),
new sst.Secret("ZEN_MODELS19"),
new sst.Secret("ZEN_MODELS20"),
2026-02-17 14:32:57 +08:00
new sst.Secret("ZEN_MODELS21"),
new sst.Secret("ZEN_MODELS22"),
new sst.Secret("ZEN_MODELS23"),
new sst.Secret("ZEN_MODELS24"),
new sst.Secret("ZEN_MODELS25"),
new sst.Secret("ZEN_MODELS26"),
new sst.Secret("ZEN_MODELS27"),
new sst.Secret("ZEN_MODELS28"),
new sst.Secret("ZEN_MODELS29"),
new sst.Secret("ZEN_MODELS30"),
2025-11-22 01:50:51 +08:00
]
2025-08-09 01:22:54 +08:00
const STRIPE_SECRET_KEY = new sst.Secret("STRIPE_SECRET_KEY")
2026-01-14 06:51:20 +08:00
const STRIPE_PUBLISHABLE_KEY = new sst.Secret("STRIPE_PUBLISHABLE_KEY")
2025-08-09 01:22:54 +08:00
const AUTH_API_URL = new sst.Linkable("AUTH_API_URL", {
properties: { value: auth.url.apply((url) => url!) },
})
const STRIPE_WEBHOOK_SECRET = new sst.Linkable("STRIPE_WEBHOOK_SECRET", {
properties: { value: stripeWebhook.secret },
})
2025-08-10 13:17:48 +08:00
////////////////
// CONSOLE
////////////////
2025-12-03 03:51:25 +08:00
const bucket = new sst.cloudflare.Bucket("ZenData")
2025-12-23 08:36:04 +08:00
const bucketNew = new sst.cloudflare.Bucket("ZenDataNew")
2025-11-24 04:21:47 +08:00
const DISCORD_INCIDENT_WEBHOOK_URL = new sst.Secret("DISCORD_INCIDENT_WEBHOOK_URL")
2025-10-02 07:34:37 +08:00
const AWS_SES_ACCESS_KEY_ID = new sst.Secret("AWS_SES_ACCESS_KEY_ID")
const AWS_SES_SECRET_ACCESS_KEY = new sst.Secret("AWS_SES_SECRET_ACCESS_KEY")
const SALESFORCE_CLIENT_ID = new sst.Secret("SALESFORCE_CLIENT_ID")
const SALESFORCE_CLIENT_SECRET = new sst.Secret("SALESFORCE_CLIENT_SECRET")
const SALESFORCE_INSTANCE_URL = new sst.Secret("SALESFORCE_INSTANCE_URL")
2026-02-11 04:55:31 +08:00
const logProcessor = new sst.cloudflare.Worker("LogProcessor", {
handler: "packages/console/function/src/log-processor.ts",
link: [SECRET.HoneycombApiKey, ...(lake?.lakeIngest ? [lake.lakeIngest] : [])],
2026-02-11 04:55:31 +08:00
})
2025-09-09 03:46:57 +08:00
2025-09-02 15:14:56 +08:00
new sst.cloudflare.x.SolidStart("Console", {
2025-09-03 11:56:10 +08:00
domain,
2025-09-18 22:59:01 +08:00
path: "packages/console/app",
2025-10-02 07:34:37 +08:00
link: [
2025-11-24 04:21:47 +08:00
bucket,
2025-12-23 08:36:04 +08:00
bucketNew,
2025-10-02 07:34:37 +08:00
database,
SECRET.UpstashRedisRestUrl,
SECRET.UpstashRedisRestToken,
2025-10-02 07:34:37 +08:00
AUTH_API_URL,
STRIPE_WEBHOOK_SECRET,
SECRET.SupportApiKey,
DISCORD_INCIDENT_WEBHOOK_URL,
SECRET.HoneycombWebhookSecret,
2025-10-02 07:34:37 +08:00
STRIPE_SECRET_KEY,
EMAILOCTOPUS_API_KEY,
AWS_SES_ACCESS_KEY_ID,
AWS_SES_SECRET_ACCESS_KEY,
SALESFORCE_CLIENT_ID,
SALESFORCE_CLIENT_SECRET,
SALESFORCE_INSTANCE_URL,
2026-01-23 01:40:42 +08:00
ZEN_BLACK_PRICE,
2026-02-23 07:41:34 +08:00
ZEN_LITE_PRICE,
2026-03-03 13:25:03 +08:00
new sst.Secret("ZEN_LIMITS"),
2026-01-13 11:33:57 +08:00
new sst.Secret("ZEN_SESSION_SECRET"),
2025-11-22 01:50:51 +08:00
...ZEN_MODELS,
2025-11-08 23:15:35 +08:00
...($dev
? [
2025-11-08 23:20:53 +08:00
new sst.Secret("CLOUDFLARE_DEFAULT_ACCOUNT_ID", process.env.CLOUDFLARE_DEFAULT_ACCOUNT_ID!),
2025-11-08 23:15:35 +08:00
new sst.Secret("CLOUDFLARE_API_TOKEN", process.env.CLOUDFLARE_API_TOKEN!),
]
: []),
2025-10-02 07:34:37 +08:00
],
2025-08-21 04:52:43 +08:00
environment: {
2025-08-30 07:34:56 +08:00
//VITE_DOCS_URL: web.url.apply((url) => url!),
//VITE_API_URL: gateway.url.apply((url) => url!),
2025-08-21 04:52:43 +08:00
VITE_AUTH_URL: auth.url.apply((url) => url!),
2026-01-14 06:51:20 +08:00
VITE_STRIPE_PUBLISHABLE_KEY: STRIPE_PUBLISHABLE_KEY.value,
2025-08-21 04:52:43 +08:00
},
2025-09-04 01:52:01 +08:00
transform: {
server: {
placement: { region: "aws:us-east-2" },
2025-09-04 01:52:01 +08:00
transform: {
worker: {
2026-02-11 04:55:31 +08:00
tailConsumers: [{ service: logProcessor.nodes.worker.scriptName }],
2025-09-04 01:52:01 +08:00
},
},
},
},
2025-08-21 04:52:43 +08:00
})
2026-05-13 09:37:34 +08:00
////////////////
// HELPERS
////////////////
export const stat = new sst.cloudflare.Worker("Stat", {
handler: "packages/console/function/src/stat.ts",
link: [database],
2026-05-13 11:19:23 +08:00
url: true,
2026-05-13 09:37:34 +08:00
})