Adding api automation workflow
This commit is contained in:
+94
-2
@@ -176,6 +176,13 @@ const billingIntervalSchema = z.enum(['monthly', 'yearly']);
|
||||
const integrationTokenScopeSchema = z.enum(['read_only', 'read_write']);
|
||||
const birdGenderSchema = z.enum(['unknown', 'male', 'female']);
|
||||
const rescueVerificationStatusSchema = z.enum(['pending', 'approved', 'rejected']);
|
||||
const rescueOnboardingSchema = z.object({
|
||||
name: z.string().trim().max(160).optional().or(z.literal('')),
|
||||
city: z.string().trim().max(120).optional().or(z.literal('')),
|
||||
state: z.string().trim().max(80).optional().or(z.literal('')),
|
||||
ein: z.string().trim().max(32).optional().or(z.literal('')),
|
||||
website: z.string().trim().url().max(2000).optional().or(z.literal('')),
|
||||
});
|
||||
|
||||
const workspaceSchema = z.object({
|
||||
name: z.string().trim().min(1).max(160),
|
||||
@@ -183,6 +190,7 @@ const workspaceSchema = z.object({
|
||||
billingEmail: z.string().trim().email().max(255).optional().or(z.literal('')),
|
||||
billingPlan: billingPlanSchema.optional(),
|
||||
billingInterval: billingIntervalSchema.optional(),
|
||||
rescueOnboarding: rescueOnboardingSchema.optional(),
|
||||
});
|
||||
|
||||
const createWorkspaceSchema = z.object({
|
||||
@@ -191,6 +199,7 @@ const createWorkspaceSchema = z.object({
|
||||
billingEmail: z.string().trim().email().max(255).optional().or(z.literal('')),
|
||||
billingPlan: billingPlanSchema.optional(),
|
||||
billingInterval: billingIntervalSchema.optional(),
|
||||
rescueOnboarding: rescueOnboardingSchema.optional(),
|
||||
});
|
||||
|
||||
const workspaceMemberSchema = z.object({
|
||||
@@ -328,6 +337,7 @@ const smtpPass = process.env.SMTP_PASS?.trim() ?? '';
|
||||
const smtpFromEmail = process.env.SMTP_FROM_EMAIL?.trim() ?? '';
|
||||
const smtpFromName = process.env.SMTP_FROM_NAME?.trim() || 'FlockPal';
|
||||
const rescueStatusNotificationEmail = process.env.RESCUE_STATUS_NOTIFICATION_EMAIL?.trim() || 'appadmin@flockpal.app';
|
||||
const rescueOnboardingWebhookUrl = 'https://n8n.blaishome.online/webhook/395cd538-5e0d-4e89-8070-9e66f571b7ee';
|
||||
const stripeSecretKey = process.env.STRIPE_SECRET_KEY?.trim() ?? '';
|
||||
const stripeWebhookSecret = process.env.STRIPE_WEBHOOK_SECRET?.trim() ?? '';
|
||||
const withBillingRedirectState = (url: string, billingState: 'success' | 'cancelled' | 'portal') => {
|
||||
@@ -1044,6 +1054,54 @@ const sendRescueStatusNotification = async ({
|
||||
return { delivered: true };
|
||||
};
|
||||
|
||||
type RescueOnboardingPayload = z.infer<typeof rescueOnboardingSchema>;
|
||||
|
||||
const sendRescueOnboardingWebhook = async ({
|
||||
action,
|
||||
workspaceId,
|
||||
flockName,
|
||||
ownerEmail,
|
||||
requestedByUserId,
|
||||
rescueOnboarding,
|
||||
}: {
|
||||
action: 'created' | 'converted';
|
||||
workspaceId: number;
|
||||
flockName: string;
|
||||
ownerEmail: string;
|
||||
requestedByUserId: string;
|
||||
rescueOnboarding: RescueOnboardingPayload;
|
||||
}) => {
|
||||
const controller = new AbortController();
|
||||
const timeout = setTimeout(() => controller.abort(), 10_000);
|
||||
|
||||
try {
|
||||
const response = await fetch(rescueOnboardingWebhookUrl, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
Name: rescueOnboarding.name,
|
||||
City: rescueOnboarding.city,
|
||||
State: rescueOnboarding.state,
|
||||
EIN: rescueOnboarding.ein,
|
||||
Website: rescueOnboarding.website,
|
||||
action,
|
||||
workspaceId,
|
||||
flockName,
|
||||
ownerEmail,
|
||||
requestedByUserId,
|
||||
submittedAt: new Date().toISOString(),
|
||||
}),
|
||||
signal: controller.signal,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Rescue onboarding webhook returned ${response.status}`);
|
||||
}
|
||||
} finally {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
};
|
||||
|
||||
const issueMagicLinkInvite = async ({
|
||||
email,
|
||||
name,
|
||||
@@ -1937,7 +1995,7 @@ app.get('/api/admin/rescue-workspaces', requireAuth, requireSessionAuth, require
|
||||
}
|
||||
});
|
||||
|
||||
app.patch('/api/admin/rescue-workspaces/:workspaceId', requireAuth, requireSessionAuth, requireAdmin, async (req: Request, res: Response, next: NextFunction) => {
|
||||
app.patch('/api/admin/rescue-workspaces/:workspaceId', requireAuth, requireAdmin, requireWriteAccess, async (req: Request, res: Response, next: NextFunction) => {
|
||||
const parsed = z.object({ rescueVerificationStatus: rescueVerificationStatusSchema }).safeParse(req.body);
|
||||
|
||||
if (!parsed.success) {
|
||||
@@ -2169,6 +2227,23 @@ app.post('/api/workspaces', requireAuth, requireSessionAuth, async (req: Request
|
||||
try {
|
||||
const workspaceId = await getNextWorkspaceId();
|
||||
const billingPlan = resolveBillingPlan(parsed.data.workspaceType, parsed.data.billingPlan);
|
||||
|
||||
if (parsed.data.workspaceType === 'rescue') {
|
||||
if (!parsed.data.rescueOnboarding) {
|
||||
res.status(400).json({ error: 'Rescue onboarding details are required.' });
|
||||
return;
|
||||
}
|
||||
|
||||
await sendRescueOnboardingWebhook({
|
||||
action: 'created',
|
||||
workspaceId,
|
||||
flockName: parsed.data.name,
|
||||
ownerEmail: req.auth!.user.email,
|
||||
requestedByUserId: req.auth!.user.id,
|
||||
rescueOnboarding: parsed.data.rescueOnboarding,
|
||||
});
|
||||
}
|
||||
|
||||
const workspace = await createWorkspace({
|
||||
id: workspaceId,
|
||||
name: parsed.data.name,
|
||||
@@ -2209,6 +2284,7 @@ app.put('/api/workspace', requireAuth, requireSessionAuth, requireWorkspaceRole(
|
||||
const currentWorkspace = req.auth!.workspace;
|
||||
const billingPlan = resolveBillingPlan(parsed.data.workspaceType, parsed.data.billingPlan ?? currentWorkspace.billing_plan);
|
||||
const billingInterval = parsed.data.workspaceType === 'rescue' ? 'monthly' : (parsed.data.billingInterval ?? currentWorkspace.billing_interval);
|
||||
const isConvertingToRescue = currentWorkspace.workspace_type !== 'rescue' && parsed.data.workspaceType === 'rescue';
|
||||
const canUpdateWorkspace =
|
||||
isAdminUser(req.auth!.user) ||
|
||||
subscriptionAllowsWrite(currentWorkspace) ||
|
||||
@@ -2225,6 +2301,22 @@ app.put('/api/workspace', requireAuth, requireSessionAuth, requireWorkspaceRole(
|
||||
return;
|
||||
}
|
||||
|
||||
if (isConvertingToRescue) {
|
||||
if (!parsed.data.rescueOnboarding) {
|
||||
res.status(400).json({ error: 'Rescue onboarding details are required.' });
|
||||
return;
|
||||
}
|
||||
|
||||
await sendRescueOnboardingWebhook({
|
||||
action: 'converted',
|
||||
workspaceId: currentWorkspace.id,
|
||||
flockName: parsed.data.name,
|
||||
ownerEmail: req.auth!.user.email,
|
||||
requestedByUserId: req.auth!.user.id,
|
||||
rescueOnboarding: parsed.data.rescueOnboarding,
|
||||
});
|
||||
}
|
||||
|
||||
const workspace = await updateWorkspace({
|
||||
workspaceId: currentWorkspace.id,
|
||||
name: parsed.data.name,
|
||||
@@ -2234,7 +2326,7 @@ app.put('/api/workspace', requireAuth, requireSessionAuth, requireWorkspaceRole(
|
||||
billingInterval,
|
||||
});
|
||||
|
||||
if (workspace?.workspace_type === 'rescue' && currentWorkspace.workspace_type !== 'rescue') {
|
||||
if (workspace?.workspace_type === 'rescue' && isConvertingToRescue) {
|
||||
await sendRescueStatusNotification({
|
||||
workspace,
|
||||
ownerEmail: req.auth!.user.email,
|
||||
|
||||
Reference in New Issue
Block a user