feat: New account system with improved team management (#273)

Description
<!-- Briefly describe the purpose and scope of your changes -->


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Introduced comprehensive account management with combined user and
team info.
  - Added advanced, context-aware logging utilities.
- Implemented invite code generation for teams with uniqueness
guarantees.
- Expanded example data for users, teams, subscriptions, sessions, and
games.

- **Enhancements**
- Refined user, team, member, and Steam account schemas for richer data
and validation.
  - Streamlined user creation, login acknowledgment, and error handling.
  - Improved API authentication and unified actor context management.
- Added persistent shared temporary volume support to API and auth
services.
- Enhanced Steam account management with create, update, and event
notifications.
- Refined team listing and serialization integrating Steam accounts as
members.
  - Simplified event, context, and logging systems.
- Updated API and auth middleware for better token handling and actor
provisioning.

- **Bug Fixes**
  - Fixed multiline log output to prefix each line with log level.

- **Removals**
- Removed machine and subscription management features, including
schemas and DB tables.
- Disabled machine-based authentication and removed related subject
schemas.
- Removed deprecated fields and legacy logic from member and team
management.
- Removed legacy event and error handling related to teams and members.

- **Chores**
  - Reorganized and cleaned exports across utility and API modules.
- Updated database schemas for users, teams, members, and Steam
accounts.
  - Improved internal code structure, imports, and error messaging.
- Moved logger patching to earlier initialization for consistent
logging.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Wanjohi
2025-05-06 07:26:59 +03:00
committed by GitHub
parent a0dc353561
commit 70d629227a
39 changed files with 1194 additions and 1480 deletions

View File

@@ -1,13 +1,14 @@
import { z } from "zod";
import { fn } from "../utils";
import { Resource } from "sst";
import { useTeam, useUserID } from "../actor";
import { Polar as PolarSdk } from "@polar-sh/sdk";
import { validateEvent } from "@polar-sh/sdk/webhooks";
import { PlanType } from "../subscription/subscription.sql";
const polar = new PolarSdk({ accessToken: Resource.PolarSecret.value, server: Resource.App.stage !== "production" ? "sandbox" : "production" });
const planType = z.enum(PlanType)
const polar = new PolarSdk({
accessToken: Resource.PolarSecret.value,
server: Resource.App.stage !== "production" ? "sandbox" : "production"
});
export namespace Polar {
export const client = polar;
@@ -16,7 +17,7 @@ export namespace Polar {
const customers = await client.customers.list({ email })
if (customers.result.items.length === 0) {
return await client.customers.create({ email })
return await client.customers.create({ email})
} else {
return customers.result.items[0]
}
@@ -28,18 +29,18 @@ export namespace Polar {
}
})
const getProductIDs = (plan: z.infer<typeof planType>) => {
switch (plan) {
case "free":
return [Resource.NestriFreeMonthly.value]
case "pro":
return [Resource.NestriProYearly.value, Resource.NestriProMonthly.value]
case "family":
return [Resource.NestriFamilyYearly.value, Resource.NestriFamilyMonthly.value]
default:
return [Resource.NestriFreeMonthly.value]
}
}
// const getProductIDs = (plan: z.infer<typeof planType>) => {
// switch (plan) {
// case "free":
// return [Resource.NestriFreeMonthly.value]
// case "pro":
// return [Resource.NestriProYearly.value, Resource.NestriProMonthly.value]
// case "family":
// return [Resource.NestriFamilyYearly.value, Resource.NestriFamilyMonthly.value]
// default:
// return [Resource.NestriFreeMonthly.value]
// }
// }
export const createPortal = fn(
z.string(),
@@ -53,44 +54,10 @@ export namespace Polar {
)
//TODO: Implement this
export const handleWebhook = async(payload: ReturnType<typeof validateEvent>) => {
export const handleWebhook = async (payload: ReturnType<typeof validateEvent>) => {
switch (payload.type) {
case "subscription.created":
const teamID = payload.data.metadata.teamID
}
}
export const createCheckout = fn(
z
.object({
planType: z.enum(PlanType),
customerEmail: z.string(),
successUrl: z.string(),
customerID: z.string(),
allowDiscountCodes: z.boolean(),
teamID: z.string()
})
.partial({
customerEmail: true,
allowDiscountCodes: true,
customerID: true,
teamID: true
}),
async (input) => {
const productIDs = getProductIDs(input.planType)
const checkoutUrl =
await client.checkouts.create({
products: productIDs,
customerEmail: input.customerEmail ?? useUserID(),
successUrl: `${input.successUrl}?checkout={CHECKOUT_ID}`,
allowDiscountCodes: input.allowDiscountCodes ?? false,
customerId: input.customerID,
customerMetadata: {
teamID: input.teamID ?? useTeam()
}
})
return checkoutUrl.url
})
}