next-workflow-builder 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,66 @@
1
+ import {
2
+ db,
3
+ workflows
4
+ } from "./chunk-3MSAF2TH.js";
5
+
6
+ // src/server/lib/metadata.ts
7
+ import { eq } from "drizzle-orm";
8
+ async function generateWorkflowMetadata({
9
+ params
10
+ }) {
11
+ const { slug } = await params;
12
+ if (slug?.[0] !== "workflows" || !slug?.[1]) {
13
+ return null;
14
+ }
15
+ const workflowId = slug[1];
16
+ let title = "Workflow";
17
+ let isPublic = false;
18
+ try {
19
+ const workflow = await db.query.workflows.findFirst({
20
+ where: eq(workflows.id, workflowId),
21
+ columns: {
22
+ name: true,
23
+ visibility: true
24
+ }
25
+ });
26
+ if (workflow) {
27
+ isPublic = workflow.visibility === "public";
28
+ if (isPublic) {
29
+ title = workflow.name;
30
+ }
31
+ }
32
+ } catch {
33
+ }
34
+ const baseUrl = process.env.NEXT_PUBLIC_APP_URL || "https://workflow-builder.dev";
35
+ const workflowUrl = `${baseUrl}/workflows/${workflowId}`;
36
+ const ogImageUrl = isPublic ? `${baseUrl}/api/og/workflow/${workflowId}` : `${baseUrl}/og-default.png`;
37
+ return {
38
+ title: `${title} | AI Workflow Builder`,
39
+ description: `View and explore the "${title}" workflow built with AI Workflow Builder.`,
40
+ openGraph: {
41
+ title: `${title} | AI Workflow Builder`,
42
+ description: `View and explore the "${title}" workflow built with AI Workflow Builder.`,
43
+ type: "website",
44
+ url: workflowUrl,
45
+ siteName: "AI Workflow Builder",
46
+ images: [
47
+ {
48
+ url: ogImageUrl,
49
+ width: 1200,
50
+ height: 630,
51
+ alt: `${title} workflow visualization`
52
+ }
53
+ ]
54
+ },
55
+ twitter: {
56
+ card: "summary_large_image",
57
+ title: `${title} | AI Workflow Builder`,
58
+ description: `View and explore the "${title}" workflow built with AI Workflow Builder.`,
59
+ images: [ogImageUrl]
60
+ }
61
+ };
62
+ }
63
+
64
+ export {
65
+ generateWorkflowMetadata
66
+ };
@@ -0,0 +1,169 @@
1
+ import {
2
+ db,
3
+ integrations
4
+ } from "./chunk-3MSAF2TH.js";
5
+
6
+ // src/server/db/integrations.ts
7
+ import "server-only";
8
+ import { and, eq, inArray } from "drizzle-orm";
9
+ import { createCipheriv, createDecipheriv, randomBytes } from "crypto";
10
+ var ALGORITHM = "aes-256-gcm";
11
+ var IV_LENGTH = 16;
12
+ var ENCRYPTION_KEY_ENV = "INTEGRATION_ENCRYPTION_KEY";
13
+ function getEncryptionKey() {
14
+ const keyHex = process.env[ENCRYPTION_KEY_ENV];
15
+ if (!keyHex) {
16
+ throw new Error(
17
+ `${ENCRYPTION_KEY_ENV} environment variable is required for encrypting integration credentials`
18
+ );
19
+ }
20
+ if (keyHex.length !== 64) {
21
+ throw new Error(
22
+ `${ENCRYPTION_KEY_ENV} must be a 64-character hex string (32 bytes)`
23
+ );
24
+ }
25
+ return Buffer.from(keyHex, "hex");
26
+ }
27
+ function encrypt(plaintext) {
28
+ const key = getEncryptionKey();
29
+ const iv = randomBytes(IV_LENGTH);
30
+ const cipher = createCipheriv(ALGORITHM, key, iv);
31
+ let encrypted = cipher.update(plaintext, "utf8", "hex");
32
+ encrypted += cipher.final("hex");
33
+ const authTag = cipher.getAuthTag();
34
+ return `${iv.toString("hex")}:${authTag.toString("hex")}:${encrypted}`;
35
+ }
36
+ function decrypt(ciphertext) {
37
+ const key = getEncryptionKey();
38
+ const parts = ciphertext.split(":");
39
+ if (parts.length !== 3) {
40
+ throw new Error("Invalid encrypted data format");
41
+ }
42
+ const iv = Buffer.from(parts[0], "hex");
43
+ const authTag = Buffer.from(parts[1], "hex");
44
+ const encrypted = parts[2];
45
+ const decipher = createDecipheriv(ALGORITHM, key, iv);
46
+ decipher.setAuthTag(authTag);
47
+ let decrypted = decipher.update(encrypted, "hex", "utf8");
48
+ decrypted += decipher.final("utf8");
49
+ return decrypted;
50
+ }
51
+ function encryptConfig(config) {
52
+ return encrypt(JSON.stringify(config));
53
+ }
54
+ function decryptConfig(encryptedConfig) {
55
+ try {
56
+ const decrypted = decrypt(encryptedConfig);
57
+ return JSON.parse(decrypted);
58
+ } catch (error) {
59
+ console.error("Failed to decrypt integration config:", error);
60
+ return {};
61
+ }
62
+ }
63
+ async function getIntegrations(userId, type) {
64
+ const conditions = [eq(integrations.userId, userId)];
65
+ if (type) {
66
+ conditions.push(eq(integrations.type, type));
67
+ }
68
+ const results = await db.select().from(integrations).where(and(...conditions));
69
+ return results.map((integration) => ({
70
+ ...integration,
71
+ config: decryptConfig(integration.config)
72
+ }));
73
+ }
74
+ async function getIntegration(integrationId, userId) {
75
+ const result = await db.select().from(integrations).where(
76
+ and(eq(integrations.id, integrationId), eq(integrations.userId, userId))
77
+ ).limit(1);
78
+ if (result.length === 0) {
79
+ return null;
80
+ }
81
+ return {
82
+ ...result[0],
83
+ config: decryptConfig(result[0].config)
84
+ };
85
+ }
86
+ async function getIntegrationById(integrationId) {
87
+ const result = await db.select().from(integrations).where(eq(integrations.id, integrationId)).limit(1);
88
+ if (result.length === 0) {
89
+ return null;
90
+ }
91
+ return {
92
+ ...result[0],
93
+ config: decryptConfig(result[0].config)
94
+ };
95
+ }
96
+ async function createIntegration(userId, name, type, config) {
97
+ const encryptedConfig = encryptConfig(config);
98
+ const [result] = await db.insert(integrations).values({
99
+ userId,
100
+ name,
101
+ type,
102
+ config: encryptedConfig
103
+ }).returning();
104
+ return {
105
+ ...result,
106
+ config
107
+ };
108
+ }
109
+ async function updateIntegration(integrationId, userId, updates) {
110
+ const updateData = {
111
+ updatedAt: /* @__PURE__ */ new Date()
112
+ };
113
+ if (updates.name !== void 0) {
114
+ updateData.name = updates.name;
115
+ }
116
+ if (updates.config !== void 0) {
117
+ updateData.config = encryptConfig(updates.config);
118
+ }
119
+ const [result] = await db.update(integrations).set(updateData).where(
120
+ and(eq(integrations.id, integrationId), eq(integrations.userId, userId))
121
+ ).returning();
122
+ if (!result) {
123
+ return null;
124
+ }
125
+ return {
126
+ ...result,
127
+ config: decryptConfig(result.config)
128
+ };
129
+ }
130
+ async function deleteIntegration(integrationId, userId) {
131
+ const result = await db.delete(integrations).where(
132
+ and(eq(integrations.id, integrationId), eq(integrations.userId, userId))
133
+ ).returning();
134
+ return result.length > 0;
135
+ }
136
+ function extractIntegrationIds(nodes) {
137
+ const integrationIds = [];
138
+ for (const node of nodes) {
139
+ const integrationId = node.data?.config?.integrationId;
140
+ if (integrationId && typeof integrationId === "string") {
141
+ integrationIds.push(integrationId);
142
+ }
143
+ }
144
+ return [...new Set(integrationIds)];
145
+ }
146
+ async function validateWorkflowIntegrations(nodes, userId) {
147
+ const integrationIds = extractIntegrationIds(nodes);
148
+ if (integrationIds.length === 0) {
149
+ return { valid: true };
150
+ }
151
+ const existingIntegrations = await db.select({ id: integrations.id, userId: integrations.userId }).from(integrations).where(inArray(integrations.id, integrationIds));
152
+ const invalidIds = existingIntegrations.filter((i) => i.userId !== userId).map((i) => i.id);
153
+ if (invalidIds.length > 0) {
154
+ return { valid: false, invalidIds };
155
+ }
156
+ return { valid: true };
157
+ }
158
+
159
+ export {
160
+ encrypt,
161
+ decrypt,
162
+ getIntegrations,
163
+ getIntegration,
164
+ getIntegrationById,
165
+ createIntegration,
166
+ updateIntegration,
167
+ deleteIntegration,
168
+ validateWorkflowIntegrations
169
+ };