deep research addressement
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { object, string, minLength, optional, picklist } from "valibot";
|
||||
import { object, string, minLength, optional, picklist, boolean } from "valibot";
|
||||
import { returnUrlSchema } from "~/lib/url-validation";
|
||||
|
||||
export const CreateCheckoutSessionSchema = object({
|
||||
@@ -31,3 +31,17 @@ export const UpgradeFromTrialSchema = object({
|
||||
plan: picklist(["basic", "plus", "premium"]),
|
||||
returnUrl: returnUrlSchema,
|
||||
});
|
||||
|
||||
export const CreateTrialSubscriptionSchema = object({
|
||||
returnUrl: returnUrlSchema,
|
||||
});
|
||||
|
||||
export const ChangeTierSchema = object({
|
||||
tier: picklist(["basic", "plus", "premium", "family_guard", "family_fortress"]),
|
||||
});
|
||||
|
||||
export const CreateFamilyCheckoutSessionSchema = object({
|
||||
tier: picklist(["family_guard", "family_fortress"]),
|
||||
returnUrl: returnUrlSchema,
|
||||
familyGroupId: string([minLength(1)]),
|
||||
});
|
||||
|
||||
@@ -34,3 +34,7 @@ export const ResolveAlertSchema = object({
|
||||
alertId: string([minLength(1)]),
|
||||
resolution: picklist(["RESOLVED", "FALSE_POSITIVE"]),
|
||||
});
|
||||
|
||||
export const FamilyThreatScoreSchema = object({
|
||||
groupId: string([minLength(1)]),
|
||||
});
|
||||
|
||||
65
web/src/server/api/schemas/family.ts
Normal file
65
web/src/server/api/schemas/family.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { object, string, email, minLength, optional, picklist, array, boolean, number } from "valibot";
|
||||
|
||||
export const CreateFamilyGroupSchema = object({
|
||||
name: string([minLength(1)]),
|
||||
planTier: optional(picklist(["family_guard", "family_fortress"])),
|
||||
});
|
||||
|
||||
export const InviteFamilyMemberSchema = object({
|
||||
email: string([email()]),
|
||||
role: optional(picklist(["admin", "member"]), "member"),
|
||||
});
|
||||
|
||||
export const AcceptInvitationSchema = object({
|
||||
token: string([minLength(1)]),
|
||||
});
|
||||
|
||||
export const ResendInvitationSchema = object({
|
||||
invitationId: string([minLength(1)]),
|
||||
});
|
||||
|
||||
export const CancelInvitationSchema = object({
|
||||
invitationId: string([minLength(1)]),
|
||||
});
|
||||
|
||||
export const RemoveFamilyMemberSchema = object({
|
||||
userId: string([minLength(1)]),
|
||||
});
|
||||
|
||||
export const LeaveFamilyGroupSchema = object({
|
||||
groupId: string([minLength(1)]),
|
||||
});
|
||||
|
||||
export const UpdateMemberRoleSchema = object({
|
||||
userId: string([minLength(1)]),
|
||||
role: picklist(["owner", "admin", "member"]),
|
||||
});
|
||||
|
||||
export const TransferOwnershipSchema = object({
|
||||
newOwnerId: string([minLength(1)]),
|
||||
});
|
||||
|
||||
export const ConfigureServicesSchema = object({
|
||||
userId: string([minLength(1)]),
|
||||
services: array(object({
|
||||
service: picklist(["darkwatch", "spamshield", "removebrokers", "hometitle", "voiceprint"]),
|
||||
enabled: boolean(),
|
||||
})),
|
||||
});
|
||||
|
||||
export const UpdateAlertPreferencesSchema = object({
|
||||
groupId: string([minLength(1)]),
|
||||
preferences: array(object({
|
||||
alertType: picklist(["critical", "security", "billing", "general"]),
|
||||
channel: picklist(["email", "push", "sms"]),
|
||||
enabled: boolean(),
|
||||
})),
|
||||
});
|
||||
|
||||
export const UpdateFamilyPlanTierSchema = object({
|
||||
planTier: picklist(["family_guard", "family_fortress"]),
|
||||
});
|
||||
|
||||
export const MemberDetailSchema = object({
|
||||
userId: string([minLength(1)]),
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
import { object, string, minLength, optional, number, picklist } from "valibot";
|
||||
import { object, string, minLength, optional, number, picklist, boolean } from "valibot";
|
||||
|
||||
export const PersonalInfoSchema = object({
|
||||
fullName: string([minLength(1)]),
|
||||
@@ -35,3 +35,17 @@ export const RemovalRequestsFilterSchema = object({
|
||||
page: optional(number(), 1),
|
||||
limit: optional(number(), 20),
|
||||
});
|
||||
|
||||
export const EnableAdapterSchema = object({
|
||||
brokerId: string([minLength(1)]),
|
||||
});
|
||||
|
||||
export const ReScanConfigSchema = object({
|
||||
cooldownDays: optional(number(), 7),
|
||||
batchSize: optional(number(), 50),
|
||||
autoReSubmit: optional(boolean(), true),
|
||||
});
|
||||
|
||||
export const CostHistorySchema = object({
|
||||
months: optional(number(), 6),
|
||||
});
|
||||
|
||||
@@ -6,6 +6,7 @@ export const CheckNumberSchema = object({
|
||||
|
||||
export const ClassifySMSSchema = object({
|
||||
text: string([minLength(1)]),
|
||||
threshold: optional(picklist(["strict", "moderate", "lenient"]), "moderate"),
|
||||
});
|
||||
|
||||
export const ClassifyCallSchema = object({
|
||||
|
||||
@@ -19,3 +19,11 @@ export const UpdateRoleSchema = object({
|
||||
userId: string(),
|
||||
role: picklist(["owner", "admin", "member"]),
|
||||
});
|
||||
|
||||
export const InviteByEmailSchema = object({
|
||||
email: string([email()]),
|
||||
});
|
||||
|
||||
export const AcceptInviteSchema = object({
|
||||
token: string(),
|
||||
});
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
optional,
|
||||
number,
|
||||
picklist,
|
||||
boolean,
|
||||
} from "valibot";
|
||||
|
||||
/**
|
||||
@@ -29,6 +30,11 @@ export const CreateEnrollmentSchema = object({
|
||||
audioBase64: string([minLength(1), maxLength(MAX_BASE64_LENGTH)]),
|
||||
});
|
||||
|
||||
export const EnrollAdditionalSampleSchema = object({
|
||||
enrollmentId: string([minLength(1)]),
|
||||
audioBase64: string([minLength(1), maxLength(MAX_BASE64_LENGTH)]),
|
||||
});
|
||||
|
||||
export const DeleteEnrollmentSchema = object({
|
||||
enrollmentId: string([minLength(1)]),
|
||||
});
|
||||
@@ -48,6 +54,51 @@ export const AnalysisResultSchema = object({
|
||||
analysisId: string([minLength(1)]),
|
||||
});
|
||||
|
||||
export const AnalysisFeedbackSchema = object({
|
||||
analysisId: string([minLength(1)]),
|
||||
isFalsePositive: boolean(),
|
||||
notes: optional(string()),
|
||||
});
|
||||
|
||||
export const JobStatusSchema = object({
|
||||
jobId: string([minLength(1)]),
|
||||
});
|
||||
|
||||
/** Call recording analysis schemas */
|
||||
export const AnalyzeCallRecordingSchema = object({
|
||||
/** Audio file as base64 (alternative to multipart upload) */
|
||||
audioBase64: optional(string([minLength(1), maxLength(MAX_BASE64_LENGTH)])),
|
||||
/** Phone number of the caller/called party */
|
||||
phoneNumber: string([minLength(1)]),
|
||||
/** Call direction */
|
||||
direction: string(),
|
||||
/** Call duration in seconds */
|
||||
duration: number(),
|
||||
/** Call start timestamp ISO string */
|
||||
callStartedAt: string(),
|
||||
/** Two-party consent state detected on device */
|
||||
consentState: optional(string()),
|
||||
});
|
||||
|
||||
export const GetCallAnalysesSchema = object({
|
||||
page: optional(number(), 1),
|
||||
limit: optional(number(), 20),
|
||||
status: optional(string()),
|
||||
});
|
||||
|
||||
export const GetCallAnalysisSchema = object({
|
||||
callRecordingId: string([minLength(1)]),
|
||||
});
|
||||
|
||||
export const UpdateCallAnalysisSettingsSchema = object({
|
||||
callAnalysisEnabled: optional(boolean()),
|
||||
autoAnalyze: optional(boolean()),
|
||||
audioRetentionDays: optional(number()),
|
||||
notifyOnSynthetic: optional(boolean()),
|
||||
autoBlockSynthetic: optional(boolean()),
|
||||
});
|
||||
|
||||
export const EmergencyHangupSchema = object({
|
||||
callRecordingId: string([minLength(1)]),
|
||||
phoneNumber: string([minLength(1)]),
|
||||
});
|
||||
|
||||
@@ -47,17 +47,20 @@ describe("SubscriptionSchema", () => {
|
||||
status: "active",
|
||||
current_period_start: 1700000000,
|
||||
current_period_end: 1702678400,
|
||||
cancel_at_period_end: "false",
|
||||
trial_end: 1700500000,
|
||||
cancel_at_period_end: false,
|
||||
metadata: { userId: "user_123" },
|
||||
items: {
|
||||
data: { price: { id: "price_basic" } },
|
||||
data: [{ price: { id: "price_basic" } }],
|
||||
},
|
||||
};
|
||||
const result = safeParse(SubscriptionSchema, data);
|
||||
expect(result.success).toBe(true);
|
||||
if (result.success) {
|
||||
expect(result.output.current_period_start).toBe(1700000000);
|
||||
expect(result.output.items?.data?.price?.id).toBe("price_basic");
|
||||
expect(result.output.items?.data?.[0]?.price?.id).toBe("price_basic");
|
||||
expect(result.output.trial_end).toBe(1700500000);
|
||||
expect(result.output.cancel_at_period_end).toBe(false);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -75,14 +78,17 @@ describe("SubscriptionSchema", () => {
|
||||
const result = safeParse(SubscriptionSchema, data);
|
||||
expect(result.success).toBe(true);
|
||||
if (result.success) {
|
||||
expect(result.output.cancel_at_period_end).toBe("false");
|
||||
expect(result.output.cancel_at_period_end).toBe(false);
|
||||
}
|
||||
});
|
||||
|
||||
it("accepts string cancel_at_period_end", () => {
|
||||
const data = { id: "sub_123", cancel_at_period_end: "true" };
|
||||
it("accepts boolean cancel_at_period_end", () => {
|
||||
const data = { id: "sub_123", cancel_at_period_end: true };
|
||||
const result = safeParse(SubscriptionSchema, data);
|
||||
expect(result.success).toBe(true);
|
||||
if (result.success) {
|
||||
expect(result.output.cancel_at_period_end).toBe(true);
|
||||
}
|
||||
});
|
||||
|
||||
it("rejects missing required id", () => {
|
||||
@@ -100,6 +106,23 @@ describe("SubscriptionSchema", () => {
|
||||
const result = safeParse(SubscriptionSchema, data);
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
it("handles items as array of price objects", () => {
|
||||
const data = {
|
||||
id: "sub_123",
|
||||
items: {
|
||||
data: [
|
||||
{ price: { id: "price_1" } },
|
||||
{ price: { id: "price_2" } },
|
||||
],
|
||||
},
|
||||
};
|
||||
const result = safeParse(SubscriptionSchema, data);
|
||||
expect(result.success).toBe(true);
|
||||
if (result.success) {
|
||||
expect(result.output.items?.data).toHaveLength(2);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("InvoiceSchema", () => {
|
||||
@@ -112,6 +135,23 @@ describe("InvoiceSchema", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("accepts full invoice data with amount and currency", () => {
|
||||
const data = {
|
||||
id: "in_123",
|
||||
subscription: "sub_456",
|
||||
amount_due: 1999,
|
||||
currency: "usd",
|
||||
status: "paid",
|
||||
};
|
||||
const result = safeParse(InvoiceSchema, data);
|
||||
expect(result.success).toBe(true);
|
||||
if (result.success) {
|
||||
expect(result.output.id).toBe("in_123");
|
||||
expect(result.output.amount_due).toBe(1999);
|
||||
expect(result.output.currency).toBe("usd");
|
||||
}
|
||||
});
|
||||
|
||||
it("accepts invoice without subscription (for partial invoices)", () => {
|
||||
const data = { id: "in_123" };
|
||||
const result = safeParse(InvoiceSchema, data);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { object, string, optional, number, type Output } from "valibot";
|
||||
import { object, string, optional, number, array, boolean, type Output } from "valibot";
|
||||
|
||||
/**
|
||||
* Validates a Stripe Checkout Session object from webhook data.
|
||||
@@ -16,10 +16,12 @@ export const CheckoutSessionSchema = object({
|
||||
/**
|
||||
* Price item inside a Stripe Subscription.
|
||||
*/
|
||||
const PriceItemSchema = object({
|
||||
price: object({
|
||||
id: string(),
|
||||
}),
|
||||
const PriceObjectSchema = object({
|
||||
id: string(),
|
||||
});
|
||||
|
||||
const SubscriptionItemSchema = object({
|
||||
price: optional(PriceObjectSchema),
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -30,7 +32,8 @@ export const SubscriptionSchema = object({
|
||||
status: optional(string()),
|
||||
current_period_start: optional(number()),
|
||||
current_period_end: optional(number()),
|
||||
cancel_at_period_end: optional(string(), "false"),
|
||||
trial_end: optional(number()),
|
||||
cancel_at_period_end: optional(boolean(), false),
|
||||
metadata: optional(
|
||||
object({
|
||||
userId: optional(string()),
|
||||
@@ -38,7 +41,7 @@ export const SubscriptionSchema = object({
|
||||
),
|
||||
items: optional(
|
||||
object({
|
||||
data: optional(PriceItemSchema),
|
||||
data: optional(array(SubscriptionItemSchema), []),
|
||||
}),
|
||||
),
|
||||
});
|
||||
@@ -47,7 +50,11 @@ export const SubscriptionSchema = object({
|
||||
* Validates a Stripe Invoice object from webhook data.
|
||||
*/
|
||||
export const InvoiceSchema = object({
|
||||
id: optional(string()),
|
||||
subscription: optional(string()),
|
||||
amount_due: optional(number()),
|
||||
currency: optional(string()),
|
||||
status: optional(string()),
|
||||
});
|
||||
|
||||
// Type exports for use in billing.service.ts
|
||||
|
||||
Reference in New Issue
Block a user