chore: generate
This commit is contained in:
@@ -421,7 +421,10 @@ export async function POST(input: APIEvent) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (body.type === "invoice.payment_succeeded") {
|
if (body.type === "invoice.payment_succeeded") {
|
||||||
if (body.data.object.billing_reason === "subscription_cycle" || body.data.object.billing_reason === "subscription_create") {
|
if (
|
||||||
|
body.data.object.billing_reason === "subscription_cycle" ||
|
||||||
|
body.data.object.billing_reason === "subscription_create"
|
||||||
|
) {
|
||||||
const invoiceID = body.data.object.id as string
|
const invoiceID = body.data.object.id as string
|
||||||
const amountInCents = body.data.object.amount_paid
|
const amountInCents = body.data.object.amount_paid
|
||||||
const customerID = body.data.object.customer as string
|
const customerID = body.data.object.customer as string
|
||||||
|
|||||||
@@ -43,9 +43,7 @@
|
|||||||
"compositePrimaryKeys": {
|
"compositePrimaryKeys": {
|
||||||
"account_id_pk": {
|
"account_id_pk": {
|
||||||
"name": "account_id_pk",
|
"name": "account_id_pk",
|
||||||
"columns": [
|
"columns": ["id"]
|
||||||
"id"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"uniqueConstraints": {},
|
"uniqueConstraints": {},
|
||||||
@@ -109,17 +107,12 @@
|
|||||||
"indexes": {
|
"indexes": {
|
||||||
"provider": {
|
"provider": {
|
||||||
"name": "provider",
|
"name": "provider",
|
||||||
"columns": [
|
"columns": ["provider", "subject"],
|
||||||
"provider",
|
|
||||||
"subject"
|
|
||||||
],
|
|
||||||
"isUnique": true
|
"isUnique": true
|
||||||
},
|
},
|
||||||
"account_id": {
|
"account_id": {
|
||||||
"name": "account_id",
|
"name": "account_id",
|
||||||
"columns": [
|
"columns": ["account_id"],
|
||||||
"account_id"
|
|
||||||
],
|
|
||||||
"isUnique": false
|
"isUnique": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -127,9 +120,7 @@
|
|||||||
"compositePrimaryKeys": {
|
"compositePrimaryKeys": {
|
||||||
"auth_id_pk": {
|
"auth_id_pk": {
|
||||||
"name": "auth_id_pk",
|
"name": "auth_id_pk",
|
||||||
"columns": [
|
"columns": ["id"]
|
||||||
"id"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"uniqueConstraints": {},
|
"uniqueConstraints": {},
|
||||||
@@ -193,9 +184,7 @@
|
|||||||
"indexes": {
|
"indexes": {
|
||||||
"time_created": {
|
"time_created": {
|
||||||
"name": "time_created",
|
"name": "time_created",
|
||||||
"columns": [
|
"columns": ["time_created"],
|
||||||
"time_created"
|
|
||||||
],
|
|
||||||
"isUnique": false
|
"isUnique": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -203,9 +192,7 @@
|
|||||||
"compositePrimaryKeys": {
|
"compositePrimaryKeys": {
|
||||||
"benchmark_id_pk": {
|
"benchmark_id_pk": {
|
||||||
"name": "benchmark_id_pk",
|
"name": "benchmark_id_pk",
|
||||||
"columns": [
|
"columns": ["id"]
|
||||||
"id"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"uniqueConstraints": {},
|
"uniqueConstraints": {},
|
||||||
@@ -388,16 +375,12 @@
|
|||||||
"indexes": {
|
"indexes": {
|
||||||
"global_customer_id": {
|
"global_customer_id": {
|
||||||
"name": "global_customer_id",
|
"name": "global_customer_id",
|
||||||
"columns": [
|
"columns": ["customer_id"],
|
||||||
"customer_id"
|
|
||||||
],
|
|
||||||
"isUnique": true
|
"isUnique": true
|
||||||
},
|
},
|
||||||
"global_subscription_id": {
|
"global_subscription_id": {
|
||||||
"name": "global_subscription_id",
|
"name": "global_subscription_id",
|
||||||
"columns": [
|
"columns": ["subscription_id"],
|
||||||
"subscription_id"
|
|
||||||
],
|
|
||||||
"isUnique": true
|
"isUnique": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -405,10 +388,7 @@
|
|||||||
"compositePrimaryKeys": {
|
"compositePrimaryKeys": {
|
||||||
"billing_workspace_id_id_pk": {
|
"billing_workspace_id_id_pk": {
|
||||||
"name": "billing_workspace_id_id_pk",
|
"name": "billing_workspace_id_id_pk",
|
||||||
"columns": [
|
"columns": ["workspace_id", "id"]
|
||||||
"workspace_id",
|
|
||||||
"id"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"uniqueConstraints": {},
|
"uniqueConstraints": {},
|
||||||
@@ -502,10 +482,7 @@
|
|||||||
"compositePrimaryKeys": {
|
"compositePrimaryKeys": {
|
||||||
"payment_workspace_id_id_pk": {
|
"payment_workspace_id_id_pk": {
|
||||||
"name": "payment_workspace_id_id_pk",
|
"name": "payment_workspace_id_id_pk",
|
||||||
"columns": [
|
"columns": ["workspace_id", "id"]
|
||||||
"workspace_id",
|
|
||||||
"id"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"uniqueConstraints": {},
|
"uniqueConstraints": {},
|
||||||
@@ -590,10 +567,7 @@
|
|||||||
"indexes": {
|
"indexes": {
|
||||||
"workspace_user_id": {
|
"workspace_user_id": {
|
||||||
"name": "workspace_user_id",
|
"name": "workspace_user_id",
|
||||||
"columns": [
|
"columns": ["workspace_id", "user_id"],
|
||||||
"workspace_id",
|
|
||||||
"user_id"
|
|
||||||
],
|
|
||||||
"isUnique": true
|
"isUnique": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -601,10 +575,7 @@
|
|||||||
"compositePrimaryKeys": {
|
"compositePrimaryKeys": {
|
||||||
"subscription_workspace_id_id_pk": {
|
"subscription_workspace_id_id_pk": {
|
||||||
"name": "subscription_workspace_id_id_pk",
|
"name": "subscription_workspace_id_id_pk",
|
||||||
"columns": [
|
"columns": ["workspace_id", "id"]
|
||||||
"workspace_id",
|
|
||||||
"id"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"uniqueConstraints": {},
|
"uniqueConstraints": {},
|
||||||
@@ -731,10 +702,7 @@
|
|||||||
"indexes": {
|
"indexes": {
|
||||||
"usage_time_created": {
|
"usage_time_created": {
|
||||||
"name": "usage_time_created",
|
"name": "usage_time_created",
|
||||||
"columns": [
|
"columns": ["workspace_id", "time_created"],
|
||||||
"workspace_id",
|
|
||||||
"time_created"
|
|
||||||
],
|
|
||||||
"isUnique": false
|
"isUnique": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -742,10 +710,7 @@
|
|||||||
"compositePrimaryKeys": {
|
"compositePrimaryKeys": {
|
||||||
"usage_workspace_id_id_pk": {
|
"usage_workspace_id_id_pk": {
|
||||||
"name": "usage_workspace_id_id_pk",
|
"name": "usage_workspace_id_id_pk",
|
||||||
"columns": [
|
"columns": ["workspace_id", "id"]
|
||||||
"workspace_id",
|
|
||||||
"id"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"uniqueConstraints": {},
|
"uniqueConstraints": {},
|
||||||
@@ -781,10 +746,7 @@
|
|||||||
"compositePrimaryKeys": {
|
"compositePrimaryKeys": {
|
||||||
"ip_rate_limit_ip_interval_pk": {
|
"ip_rate_limit_ip_interval_pk": {
|
||||||
"name": "ip_rate_limit_ip_interval_pk",
|
"name": "ip_rate_limit_ip_interval_pk",
|
||||||
"columns": [
|
"columns": ["ip", "interval"]
|
||||||
"ip",
|
|
||||||
"interval"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"uniqueConstraints": {},
|
"uniqueConstraints": {},
|
||||||
@@ -836,9 +798,7 @@
|
|||||||
"compositePrimaryKeys": {
|
"compositePrimaryKeys": {
|
||||||
"ip_ip_pk": {
|
"ip_ip_pk": {
|
||||||
"name": "ip_ip_pk",
|
"name": "ip_ip_pk",
|
||||||
"columns": [
|
"columns": ["ip"]
|
||||||
"ip"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"uniqueConstraints": {},
|
"uniqueConstraints": {},
|
||||||
@@ -916,9 +876,7 @@
|
|||||||
"indexes": {
|
"indexes": {
|
||||||
"global_key": {
|
"global_key": {
|
||||||
"name": "global_key",
|
"name": "global_key",
|
||||||
"columns": [
|
"columns": ["key"],
|
||||||
"key"
|
|
||||||
],
|
|
||||||
"isUnique": true
|
"isUnique": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -926,10 +884,7 @@
|
|||||||
"compositePrimaryKeys": {
|
"compositePrimaryKeys": {
|
||||||
"key_workspace_id_id_pk": {
|
"key_workspace_id_id_pk": {
|
||||||
"name": "key_workspace_id_id_pk",
|
"name": "key_workspace_id_id_pk",
|
||||||
"columns": [
|
"columns": ["workspace_id", "id"]
|
||||||
"workspace_id",
|
|
||||||
"id"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"uniqueConstraints": {},
|
"uniqueConstraints": {},
|
||||||
@@ -986,10 +941,7 @@
|
|||||||
"indexes": {
|
"indexes": {
|
||||||
"model_workspace_model": {
|
"model_workspace_model": {
|
||||||
"name": "model_workspace_model",
|
"name": "model_workspace_model",
|
||||||
"columns": [
|
"columns": ["workspace_id", "model"],
|
||||||
"workspace_id",
|
|
||||||
"model"
|
|
||||||
],
|
|
||||||
"isUnique": true
|
"isUnique": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -997,10 +949,7 @@
|
|||||||
"compositePrimaryKeys": {
|
"compositePrimaryKeys": {
|
||||||
"model_workspace_id_id_pk": {
|
"model_workspace_id_id_pk": {
|
||||||
"name": "model_workspace_id_id_pk",
|
"name": "model_workspace_id_id_pk",
|
||||||
"columns": [
|
"columns": ["workspace_id", "id"]
|
||||||
"workspace_id",
|
|
||||||
"id"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"uniqueConstraints": {},
|
"uniqueConstraints": {},
|
||||||
@@ -1064,10 +1013,7 @@
|
|||||||
"indexes": {
|
"indexes": {
|
||||||
"workspace_provider": {
|
"workspace_provider": {
|
||||||
"name": "workspace_provider",
|
"name": "workspace_provider",
|
||||||
"columns": [
|
"columns": ["workspace_id", "provider"],
|
||||||
"workspace_id",
|
|
||||||
"provider"
|
|
||||||
],
|
|
||||||
"isUnique": true
|
"isUnique": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -1075,10 +1021,7 @@
|
|||||||
"compositePrimaryKeys": {
|
"compositePrimaryKeys": {
|
||||||
"provider_workspace_id_id_pk": {
|
"provider_workspace_id_id_pk": {
|
||||||
"name": "provider_workspace_id_id_pk",
|
"name": "provider_workspace_id_id_pk",
|
||||||
"columns": [
|
"columns": ["workspace_id", "id"]
|
||||||
"workspace_id",
|
|
||||||
"id"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"uniqueConstraints": {},
|
"uniqueConstraints": {},
|
||||||
@@ -1191,32 +1134,22 @@
|
|||||||
"indexes": {
|
"indexes": {
|
||||||
"user_account_id": {
|
"user_account_id": {
|
||||||
"name": "user_account_id",
|
"name": "user_account_id",
|
||||||
"columns": [
|
"columns": ["workspace_id", "account_id"],
|
||||||
"workspace_id",
|
|
||||||
"account_id"
|
|
||||||
],
|
|
||||||
"isUnique": true
|
"isUnique": true
|
||||||
},
|
},
|
||||||
"user_email": {
|
"user_email": {
|
||||||
"name": "user_email",
|
"name": "user_email",
|
||||||
"columns": [
|
"columns": ["workspace_id", "email"],
|
||||||
"workspace_id",
|
|
||||||
"email"
|
|
||||||
],
|
|
||||||
"isUnique": true
|
"isUnique": true
|
||||||
},
|
},
|
||||||
"global_account_id": {
|
"global_account_id": {
|
||||||
"name": "global_account_id",
|
"name": "global_account_id",
|
||||||
"columns": [
|
"columns": ["account_id"],
|
||||||
"account_id"
|
|
||||||
],
|
|
||||||
"isUnique": false
|
"isUnique": false
|
||||||
},
|
},
|
||||||
"global_email": {
|
"global_email": {
|
||||||
"name": "global_email",
|
"name": "global_email",
|
||||||
"columns": [
|
"columns": ["email"],
|
||||||
"email"
|
|
||||||
],
|
|
||||||
"isUnique": false
|
"isUnique": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -1224,10 +1157,7 @@
|
|||||||
"compositePrimaryKeys": {
|
"compositePrimaryKeys": {
|
||||||
"user_workspace_id_id_pk": {
|
"user_workspace_id_id_pk": {
|
||||||
"name": "user_workspace_id_id_pk",
|
"name": "user_workspace_id_id_pk",
|
||||||
"columns": [
|
"columns": ["workspace_id", "id"]
|
||||||
"workspace_id",
|
|
||||||
"id"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"uniqueConstraints": {},
|
"uniqueConstraints": {},
|
||||||
@@ -1284,9 +1214,7 @@
|
|||||||
"indexes": {
|
"indexes": {
|
||||||
"slug": {
|
"slug": {
|
||||||
"name": "slug",
|
"name": "slug",
|
||||||
"columns": [
|
"columns": ["slug"],
|
||||||
"slug"
|
|
||||||
],
|
|
||||||
"isUnique": true
|
"isUnique": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -1294,9 +1222,7 @@
|
|||||||
"compositePrimaryKeys": {
|
"compositePrimaryKeys": {
|
||||||
"workspace_id": {
|
"workspace_id": {
|
||||||
"name": "workspace_id",
|
"name": "workspace_id",
|
||||||
"columns": [
|
"columns": ["id"]
|
||||||
"id"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"uniqueConstraints": {},
|
"uniqueConstraints": {},
|
||||||
|
|||||||
@@ -290,65 +290,68 @@ export namespace Billing {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
export const subscribe = fn(z.object({
|
export const subscribe = fn(
|
||||||
seats: z.number(),
|
z.object({
|
||||||
coupon: z.string().optional(),
|
seats: z.number(),
|
||||||
}), async ({ seats, coupon }) => {
|
coupon: z.string().optional(),
|
||||||
const user = Actor.assert("user")
|
}),
|
||||||
const billing = await Database.use((tx) =>
|
async ({ seats, coupon }) => {
|
||||||
tx
|
const user = Actor.assert("user")
|
||||||
.select({
|
const billing = await Database.use((tx) =>
|
||||||
customerID: BillingTable.customerID,
|
tx
|
||||||
paymentMethodID: BillingTable.paymentMethodID,
|
.select({
|
||||||
subscriptionID: BillingTable.subscriptionID,
|
customerID: BillingTable.customerID,
|
||||||
subscriptionPlan: BillingTable.subscriptionPlan,
|
paymentMethodID: BillingTable.paymentMethodID,
|
||||||
timeSubscriptionSelected: BillingTable.timeSubscriptionSelected,
|
subscriptionID: BillingTable.subscriptionID,
|
||||||
})
|
subscriptionPlan: BillingTable.subscriptionPlan,
|
||||||
.from(BillingTable)
|
timeSubscriptionSelected: BillingTable.timeSubscriptionSelected,
|
||||||
.where(eq(BillingTable.workspaceID, Actor.workspace()))
|
})
|
||||||
.then((rows) => rows[0]),
|
.from(BillingTable)
|
||||||
)
|
.where(eq(BillingTable.workspaceID, Actor.workspace()))
|
||||||
|
.then((rows) => rows[0]),
|
||||||
|
)
|
||||||
|
|
||||||
if (!billing) throw new Error("Billing record not found")
|
if (!billing) throw new Error("Billing record not found")
|
||||||
if (!billing.timeSubscriptionSelected) throw new Error("Not selected for subscription")
|
if (!billing.timeSubscriptionSelected) throw new Error("Not selected for subscription")
|
||||||
if (billing.subscriptionID) throw new Error("Already subscribed")
|
if (billing.subscriptionID) throw new Error("Already subscribed")
|
||||||
if (!billing.customerID) throw new Error("No customer ID")
|
if (!billing.customerID) throw new Error("No customer ID")
|
||||||
if (!billing.paymentMethodID) throw new Error("No payment method")
|
if (!billing.paymentMethodID) throw new Error("No payment method")
|
||||||
if (!billing.subscriptionPlan) throw new Error("No subscription plan")
|
if (!billing.subscriptionPlan) throw new Error("No subscription plan")
|
||||||
|
|
||||||
const subscription = await Billing.stripe().subscriptions.create({
|
const subscription = await Billing.stripe().subscriptions.create({
|
||||||
customer: billing.customerID,
|
customer: billing.customerID,
|
||||||
default_payment_method: billing.paymentMethodID,
|
default_payment_method: billing.paymentMethodID,
|
||||||
items: [{ price: BlackData.planToPriceID({ plan: billing.subscriptionPlan }) }],
|
items: [{ price: BlackData.planToPriceID({ plan: billing.subscriptionPlan }) }],
|
||||||
metadata: {
|
metadata: {
|
||||||
workspaceID: Actor.workspace(),
|
workspaceID: Actor.workspace(),
|
||||||
},
|
},
|
||||||
})
|
|
||||||
|
|
||||||
await Database.transaction(async (tx) => {
|
|
||||||
await tx
|
|
||||||
.update(BillingTable)
|
|
||||||
.set({
|
|
||||||
subscriptionID: subscription.id,
|
|
||||||
subscription: {
|
|
||||||
status: "subscribed",
|
|
||||||
coupon,
|
|
||||||
seats,
|
|
||||||
plan: billing.subscriptionPlan!,
|
|
||||||
},
|
|
||||||
subscriptionPlan: null,
|
|
||||||
timeSubscriptionBooked: null,
|
|
||||||
timeSubscriptionSelected: null,
|
|
||||||
})
|
|
||||||
.where(eq(BillingTable.workspaceID, Actor.workspace()))
|
|
||||||
|
|
||||||
await tx.insert(SubscriptionTable).values({
|
|
||||||
workspaceID: Actor.workspace(),
|
|
||||||
id: Identifier.create("subscription"),
|
|
||||||
userID: user.properties.userID,
|
|
||||||
})
|
})
|
||||||
})
|
|
||||||
|
|
||||||
return subscription.id
|
await Database.transaction(async (tx) => {
|
||||||
})
|
await tx
|
||||||
|
.update(BillingTable)
|
||||||
|
.set({
|
||||||
|
subscriptionID: subscription.id,
|
||||||
|
subscription: {
|
||||||
|
status: "subscribed",
|
||||||
|
coupon,
|
||||||
|
seats,
|
||||||
|
plan: billing.subscriptionPlan!,
|
||||||
|
},
|
||||||
|
subscriptionPlan: null,
|
||||||
|
timeSubscriptionBooked: null,
|
||||||
|
timeSubscriptionSelected: null,
|
||||||
|
})
|
||||||
|
.where(eq(BillingTable.workspaceID, Actor.workspace()))
|
||||||
|
|
||||||
|
await tx.insert(SubscriptionTable).values({
|
||||||
|
workspaceID: Actor.workspace(),
|
||||||
|
id: Identifier.create("subscription"),
|
||||||
|
userID: user.properties.userID,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
return subscription.id
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,28 +28,37 @@ export namespace BlackData {
|
|||||||
return input
|
return input
|
||||||
})
|
})
|
||||||
|
|
||||||
export const getLimits = fn(z.object({
|
export const getLimits = fn(
|
||||||
|
z.object({
|
||||||
plan: z.enum(SubscriptionPlan),
|
plan: z.enum(SubscriptionPlan),
|
||||||
}), ({ plan }) => {
|
}),
|
||||||
const json = JSON.parse(Resource.ZEN_BLACK_LIMITS.value)
|
({ plan }) => {
|
||||||
return Schema.parse(json)[plan]
|
const json = JSON.parse(Resource.ZEN_BLACK_LIMITS.value)
|
||||||
})
|
return Schema.parse(json)[plan]
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
export const planToPriceID = fn(z.object({
|
export const planToPriceID = fn(
|
||||||
|
z.object({
|
||||||
plan: z.enum(SubscriptionPlan),
|
plan: z.enum(SubscriptionPlan),
|
||||||
}), ({ plan }) => {
|
}),
|
||||||
if (plan === "200") return Resource.ZEN_BLACK_PRICE.plan200
|
({ plan }) => {
|
||||||
if (plan === "100") return Resource.ZEN_BLACK_PRICE.plan100
|
if (plan === "200") return Resource.ZEN_BLACK_PRICE.plan200
|
||||||
return Resource.ZEN_BLACK_PRICE.plan20
|
if (plan === "100") return Resource.ZEN_BLACK_PRICE.plan100
|
||||||
})
|
return Resource.ZEN_BLACK_PRICE.plan20
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
export const priceIDToPlan = fn(z.object({
|
export const priceIDToPlan = fn(
|
||||||
priceID: z.string(),
|
z.object({
|
||||||
}), ({ priceID }) => {
|
priceID: z.string(),
|
||||||
if (priceID === Resource.ZEN_BLACK_PRICE.plan200) return "200"
|
}),
|
||||||
if (priceID === Resource.ZEN_BLACK_PRICE.plan100) return "100"
|
({ priceID }) => {
|
||||||
return "20"
|
if (priceID === Resource.ZEN_BLACK_PRICE.plan200) return "200"
|
||||||
})
|
if (priceID === Resource.ZEN_BLACK_PRICE.plan100) return "100"
|
||||||
|
return "20"
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export namespace Black {
|
export namespace Black {
|
||||||
|
|||||||
Reference in New Issue
Block a user