console.ts 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. import { deployAws, domain } from "./stage"
  2. import { EMAILOCTOPUS_API_KEY } from "./app"
  3. import { SECRET } from "./secret"
  4. const lake = deployAws ? await import("./lake") : undefined
  5. ////////////////
  6. // DATABASE
  7. ////////////////
  8. const cluster = planetscale.getDatabaseOutput({
  9. name: "opencode",
  10. organization: "anomalyco",
  11. })
  12. const branch =
  13. $app.stage === "production"
  14. ? planetscale.getBranchOutput({
  15. name: "production",
  16. organization: cluster.organization,
  17. database: cluster.name,
  18. })
  19. : new planetscale.Branch("DatabaseBranch", {
  20. database: cluster.name,
  21. organization: cluster.organization,
  22. name: $app.stage,
  23. parentBranch: "production",
  24. })
  25. const password = new planetscale.Password("DatabasePassword", {
  26. name: $app.stage,
  27. database: cluster.name,
  28. organization: cluster.organization,
  29. branch: branch.name,
  30. })
  31. export const database = new sst.Linkable("Database", {
  32. properties: {
  33. host: password.accessHostUrl,
  34. database: cluster.name,
  35. username: password.username,
  36. password: password.plaintext,
  37. port: 3306,
  38. },
  39. })
  40. new sst.x.DevCommand("Studio", {
  41. link: [database],
  42. dev: {
  43. command: "bun db studio",
  44. directory: "packages/console/core",
  45. autostart: true,
  46. },
  47. })
  48. ////////////////
  49. // AUTH
  50. ////////////////
  51. const GITHUB_CLIENT_ID_CONSOLE = new sst.Secret("GITHUB_CLIENT_ID_CONSOLE")
  52. const GITHUB_CLIENT_SECRET_CONSOLE = new sst.Secret("GITHUB_CLIENT_SECRET_CONSOLE")
  53. const GOOGLE_CLIENT_ID = new sst.Secret("GOOGLE_CLIENT_ID")
  54. const authStorage = new sst.cloudflare.Kv("AuthStorage")
  55. export const auth = new sst.cloudflare.Worker("AuthApi", {
  56. domain: `auth.${domain}`,
  57. handler: "packages/console/function/src/auth.ts",
  58. url: true,
  59. link: [database, authStorage, GITHUB_CLIENT_ID_CONSOLE, GITHUB_CLIENT_SECRET_CONSOLE, GOOGLE_CLIENT_ID],
  60. })
  61. ////////////////
  62. // GATEWAY
  63. ////////////////
  64. export const stripeWebhook = new stripe.WebhookEndpoint("StripeWebhookEndpoint", {
  65. url: $interpolate`https://${domain}/stripe/webhook`,
  66. enabledEvents: [
  67. "checkout.session.async_payment_failed",
  68. "checkout.session.async_payment_succeeded",
  69. "checkout.session.completed",
  70. "checkout.session.expired",
  71. "charge.refunded",
  72. "invoice.payment_succeeded",
  73. "invoice.payment_failed",
  74. "invoice.payment_action_required",
  75. "customer.created",
  76. "customer.deleted",
  77. "customer.updated",
  78. "customer.discount.created",
  79. "customer.discount.deleted",
  80. "customer.discount.updated",
  81. "customer.source.created",
  82. "customer.source.deleted",
  83. "customer.source.expiring",
  84. "customer.source.updated",
  85. "customer.subscription.created",
  86. "customer.subscription.deleted",
  87. "customer.subscription.paused",
  88. "customer.subscription.pending_update_applied",
  89. "customer.subscription.pending_update_expired",
  90. "customer.subscription.resumed",
  91. "customer.subscription.trial_will_end",
  92. "customer.subscription.updated",
  93. ],
  94. })
  95. const zenLiteProduct = new stripe.Product("ZenLite", {
  96. name: "OpenCode Go",
  97. })
  98. const zenLiteCouponFirstMonth50 = new stripe.Coupon("ZenLiteCouponFirstMonth50", {
  99. name: "First month 50% off",
  100. percentOff: 50,
  101. appliesToProducts: [zenLiteProduct.id],
  102. duration: "once",
  103. })
  104. const zenLiteCouponFirstMonth100 = new stripe.Coupon("ZenLiteCouponFirstMonth100", {
  105. name: "First month 100% off",
  106. percentOff: 100,
  107. appliesToProducts: [zenLiteProduct.id],
  108. duration: "once",
  109. })
  110. const zenLiteCouponThreeMonths100 = new stripe.Coupon("ZenLiteCoupon3Months100", {
  111. name: "3 months 100% off",
  112. percentOff: 100,
  113. appliesToProducts: [zenLiteProduct.id],
  114. duration: "repeating",
  115. durationInMonths: 3,
  116. })
  117. const zenLiteCouponSixMonths100 = new stripe.Coupon("ZenLiteCoupon6Months100", {
  118. name: "6 months 100% off",
  119. percentOff: 100,
  120. appliesToProducts: [zenLiteProduct.id],
  121. duration: "repeating",
  122. durationInMonths: 6,
  123. })
  124. const zenLiteCouponTwelveMonths100 = new stripe.Coupon("ZenLiteCoupon12Months100", {
  125. name: "12 months 100% off",
  126. percentOff: 100,
  127. appliesToProducts: [zenLiteProduct.id],
  128. duration: "repeating",
  129. durationInMonths: 12,
  130. })
  131. const zenLitePrice = new stripe.Price("ZenLitePrice", {
  132. product: zenLiteProduct.id,
  133. currency: "usd",
  134. recurring: {
  135. interval: "month",
  136. intervalCount: 1,
  137. },
  138. unitAmount: 1000,
  139. })
  140. const ZEN_LITE_PRICE = new sst.Linkable("ZEN_LITE_PRICE", {
  141. properties: {
  142. product: zenLiteProduct.id,
  143. price: zenLitePrice.id,
  144. priceInr: 92900,
  145. firstMonth50Coupon: zenLiteCouponFirstMonth50.id,
  146. firstMonth100Coupon: zenLiteCouponFirstMonth100.id,
  147. threeMonths100Coupon: zenLiteCouponThreeMonths100.id,
  148. sixMonths100Coupon: zenLiteCouponSixMonths100.id,
  149. twelveMonths100Coupon: zenLiteCouponTwelveMonths100.id,
  150. },
  151. })
  152. const zenBlackProduct = new stripe.Product("ZenBlack", {
  153. name: "OpenCode Black",
  154. })
  155. const zenBlackPriceProps = {
  156. product: zenBlackProduct.id,
  157. currency: "usd",
  158. recurring: {
  159. interval: "month",
  160. intervalCount: 1,
  161. },
  162. }
  163. const zenBlackPrice200 = new stripe.Price("ZenBlackPrice", { ...zenBlackPriceProps, unitAmount: 20000 })
  164. const zenBlackPrice100 = new stripe.Price("ZenBlack100Price", { ...zenBlackPriceProps, unitAmount: 10000 })
  165. const zenBlackPrice20 = new stripe.Price("ZenBlack20Price", { ...zenBlackPriceProps, unitAmount: 2000 })
  166. const ZEN_BLACK_PRICE = new sst.Linkable("ZEN_BLACK_PRICE", {
  167. properties: {
  168. product: zenBlackProduct.id,
  169. plan200: zenBlackPrice200.id,
  170. plan100: zenBlackPrice100.id,
  171. plan20: zenBlackPrice20.id,
  172. },
  173. })
  174. const ZEN_MODELS = [
  175. new sst.Secret("ZEN_MODELS1"),
  176. new sst.Secret("ZEN_MODELS2"),
  177. new sst.Secret("ZEN_MODELS3"),
  178. new sst.Secret("ZEN_MODELS4"),
  179. new sst.Secret("ZEN_MODELS5"),
  180. new sst.Secret("ZEN_MODELS6"),
  181. new sst.Secret("ZEN_MODELS7"),
  182. new sst.Secret("ZEN_MODELS8"),
  183. new sst.Secret("ZEN_MODELS9"),
  184. new sst.Secret("ZEN_MODELS10"),
  185. new sst.Secret("ZEN_MODELS11"),
  186. new sst.Secret("ZEN_MODELS12"),
  187. new sst.Secret("ZEN_MODELS13"),
  188. new sst.Secret("ZEN_MODELS14"),
  189. new sst.Secret("ZEN_MODELS15"),
  190. new sst.Secret("ZEN_MODELS16"),
  191. new sst.Secret("ZEN_MODELS17"),
  192. new sst.Secret("ZEN_MODELS18"),
  193. new sst.Secret("ZEN_MODELS19"),
  194. new sst.Secret("ZEN_MODELS20"),
  195. new sst.Secret("ZEN_MODELS21"),
  196. new sst.Secret("ZEN_MODELS22"),
  197. new sst.Secret("ZEN_MODELS23"),
  198. new sst.Secret("ZEN_MODELS24"),
  199. new sst.Secret("ZEN_MODELS25"),
  200. new sst.Secret("ZEN_MODELS26"),
  201. new sst.Secret("ZEN_MODELS27"),
  202. new sst.Secret("ZEN_MODELS28"),
  203. new sst.Secret("ZEN_MODELS29"),
  204. new sst.Secret("ZEN_MODELS30"),
  205. ]
  206. const STRIPE_SECRET_KEY = new sst.Secret("STRIPE_SECRET_KEY")
  207. const STRIPE_PUBLISHABLE_KEY = new sst.Secret("STRIPE_PUBLISHABLE_KEY")
  208. const AUTH_API_URL = new sst.Linkable("AUTH_API_URL", {
  209. properties: { value: auth.url.apply((url) => url!) },
  210. })
  211. const STRIPE_WEBHOOK_SECRET = new sst.Linkable("STRIPE_WEBHOOK_SECRET", {
  212. properties: { value: stripeWebhook.secret },
  213. })
  214. ////////////////
  215. // CONSOLE
  216. ////////////////
  217. const bucket = new sst.cloudflare.Bucket("ZenData")
  218. const bucketNew = new sst.cloudflare.Bucket("ZenDataNew")
  219. const DISCORD_INCIDENT_WEBHOOK_URL = new sst.Secret("DISCORD_INCIDENT_WEBHOOK_URL")
  220. const AWS_SES_ACCESS_KEY_ID = new sst.Secret("AWS_SES_ACCESS_KEY_ID")
  221. const AWS_SES_SECRET_ACCESS_KEY = new sst.Secret("AWS_SES_SECRET_ACCESS_KEY")
  222. const SALESFORCE_CLIENT_ID = new sst.Secret("SALESFORCE_CLIENT_ID")
  223. const SALESFORCE_CLIENT_SECRET = new sst.Secret("SALESFORCE_CLIENT_SECRET")
  224. const SALESFORCE_INSTANCE_URL = new sst.Secret("SALESFORCE_INSTANCE_URL")
  225. const logProcessor = new sst.cloudflare.Worker("LogProcessor", {
  226. handler: "packages/console/function/src/log-processor.ts",
  227. link: [SECRET.HoneycombApiKey, ...(lake?.lakeIngest ? [lake.lakeIngest] : [])],
  228. })
  229. new sst.cloudflare.x.SolidStart("Console", {
  230. domain,
  231. path: "packages/console/app",
  232. link: [
  233. bucket,
  234. bucketNew,
  235. database,
  236. SECRET.UpstashRedisRestUrl,
  237. SECRET.UpstashRedisRestToken,
  238. AUTH_API_URL,
  239. STRIPE_WEBHOOK_SECRET,
  240. SECRET.SupportApiKey,
  241. DISCORD_INCIDENT_WEBHOOK_URL,
  242. SECRET.HoneycombWebhookSecret,
  243. STRIPE_SECRET_KEY,
  244. EMAILOCTOPUS_API_KEY,
  245. AWS_SES_ACCESS_KEY_ID,
  246. AWS_SES_SECRET_ACCESS_KEY,
  247. SALESFORCE_CLIENT_ID,
  248. SALESFORCE_CLIENT_SECRET,
  249. SALESFORCE_INSTANCE_URL,
  250. ZEN_BLACK_PRICE,
  251. ZEN_LITE_PRICE,
  252. new sst.Secret("ZEN_LIMITS"),
  253. new sst.Secret("ZEN_SESSION_SECRET"),
  254. ...ZEN_MODELS,
  255. ...($dev
  256. ? [
  257. new sst.Secret("CLOUDFLARE_DEFAULT_ACCOUNT_ID", process.env.CLOUDFLARE_DEFAULT_ACCOUNT_ID!),
  258. new sst.Secret("CLOUDFLARE_API_TOKEN", process.env.CLOUDFLARE_API_TOKEN!),
  259. ]
  260. : []),
  261. ],
  262. environment: {
  263. //VITE_DOCS_URL: web.url.apply((url) => url!),
  264. //VITE_API_URL: gateway.url.apply((url) => url!),
  265. VITE_AUTH_URL: auth.url.apply((url) => url!),
  266. VITE_STRIPE_PUBLISHABLE_KEY: STRIPE_PUBLISHABLE_KEY.value,
  267. },
  268. transform: {
  269. server: {
  270. placement: { region: "aws:us-east-2" },
  271. transform: {
  272. worker: {
  273. tailConsumers: [{ service: logProcessor.nodes.worker.scriptName }],
  274. },
  275. },
  276. },
  277. },
  278. })
  279. ////////////////
  280. // HELPERS
  281. ////////////////
  282. export const stat = new sst.cloudflare.Worker("Stat", {
  283. handler: "packages/console/function/src/stat.ts",
  284. link: [database],
  285. url: true,
  286. })