arcadiasuite/server/lowcode/routes.ts

357 lines
11 KiB
TypeScript

import { Router } from "express";
import { db } from "../../db/index";
import { z } from "zod";
import { sql, eq, and, asc } from "drizzle-orm";
const router = Router();
const createDocTypeSchema = z.object({
name: z.string().min(1),
label: z.string().min(1),
module: z.string().optional(),
description: z.string().optional(),
icon: z.string().default("FileText"),
color: z.string().default("blue"),
isSubmittable: z.boolean().default(false),
isSingle: z.boolean().default(false),
isTree: z.boolean().default(false),
hasWebView: z.boolean().default(true),
});
const createFieldSchema = z.object({
docTypeId: z.number(),
fieldName: z.string().min(1),
label: z.string().min(1),
fieldType: z.string().min(1),
options: z.string().optional(),
defaultValue: z.string().optional(),
mandatory: z.boolean().default(false),
inListView: z.boolean().default(false),
inFilter: z.boolean().default(false),
sortOrder: z.number().default(0),
section: z.string().optional(),
placeholder: z.string().optional(),
helpText: z.string().optional(),
});
router.get("/doctypes", async (req, res) => {
try {
const result = await db.execute(sql`
SELECT * FROM arc_doctypes
WHERE status = 'active'
ORDER BY module, label
`);
res.json(result.rows);
} catch (error) {
console.error("Error fetching doctypes:", error);
res.status(500).json({ error: "Failed to fetch doctypes" });
}
});
router.get("/doctypes/:id", async (req, res) => {
try {
const { id } = req.params;
const result = await db.execute(sql`
SELECT * FROM arc_doctypes WHERE id = ${parseInt(id)}
`);
if (result.rows.length === 0) {
return res.status(404).json({ error: "DocType not found" });
}
res.json(result.rows[0]);
} catch (error) {
console.error("Error fetching doctype:", error);
res.status(500).json({ error: "Failed to fetch doctype" });
}
});
router.post("/doctypes", async (req, res) => {
try {
const data = createDocTypeSchema.parse(req.body);
const result = await db.execute(sql`
INSERT INTO arc_doctypes (name, label, module, description, icon, color, is_submittable, is_single, is_tree, has_web_view)
VALUES (${data.name}, ${data.label}, ${data.module || null}, ${data.description || null},
${data.icon}, ${data.color}, ${data.isSubmittable}, ${data.isSingle}, ${data.isTree}, ${data.hasWebView})
RETURNING *
`);
res.json(result.rows[0]);
} catch (error) {
console.error("Error creating doctype:", error);
res.status(500).json({ error: "Failed to create doctype" });
}
});
router.put("/doctypes/:id", async (req, res) => {
try {
const { id } = req.params;
const data = createDocTypeSchema.partial().parse(req.body);
const result = await db.execute(sql`
UPDATE arc_doctypes SET
name = COALESCE(${data.name}, name),
label = COALESCE(${data.label}, label),
module = COALESCE(${data.module}, module),
description = COALESCE(${data.description}, description),
icon = COALESCE(${data.icon}, icon),
color = COALESCE(${data.color}, color),
updated_at = CURRENT_TIMESTAMP
WHERE id = ${parseInt(id)}
RETURNING *
`);
res.json(result.rows[0]);
} catch (error) {
console.error("Error updating doctype:", error);
res.status(500).json({ error: "Failed to update doctype" });
}
});
router.delete("/doctypes/:id", async (req, res) => {
try {
const { id } = req.params;
await db.execute(sql`
UPDATE arc_doctypes SET status = 'deleted' WHERE id = ${parseInt(id)}
`);
res.json({ success: true });
} catch (error) {
console.error("Error deleting doctype:", error);
res.status(500).json({ error: "Failed to delete doctype" });
}
});
router.get("/doctypes/:id/fields", async (req, res) => {
try {
const { id } = req.params;
const result = await db.execute(sql`
SELECT * FROM arc_fields
WHERE doctype_id = ${parseInt(id)}
ORDER BY sort_order, id
`);
res.json(result.rows);
} catch (error) {
console.error("Error fetching fields:", error);
res.status(500).json({ error: "Failed to fetch fields" });
}
});
router.post("/fields", async (req, res) => {
try {
const data = createFieldSchema.parse(req.body);
const result = await db.execute(sql`
INSERT INTO arc_fields (
doctype_id, field_name, label, field_type, options, default_value,
mandatory, in_list_view, in_filter, sort_order, section, placeholder, help_text
)
VALUES (
${data.docTypeId}, ${data.fieldName}, ${data.label}, ${data.fieldType},
${data.options || null}, ${data.defaultValue || null}, ${data.mandatory},
${data.inListView}, ${data.inFilter}, ${data.sortOrder},
${data.section || null}, ${data.placeholder || null}, ${data.helpText || null}
)
RETURNING *
`);
res.json(result.rows[0]);
} catch (error) {
console.error("Error creating field:", error);
res.status(500).json({ error: "Failed to create field" });
}
});
router.put("/fields/:id", async (req, res) => {
try {
const { id } = req.params;
const data = createFieldSchema.partial().parse(req.body);
const result = await db.execute(sql`
UPDATE arc_fields SET
field_name = COALESCE(${data.fieldName}, field_name),
label = COALESCE(${data.label}, label),
field_type = COALESCE(${data.fieldType}, field_type),
options = COALESCE(${data.options}, options),
mandatory = COALESCE(${data.mandatory}, mandatory),
in_list_view = COALESCE(${data.inListView}, in_list_view),
in_filter = COALESCE(${data.inFilter}, in_filter),
sort_order = COALESCE(${data.sortOrder}, sort_order),
section = COALESCE(${data.section}, section),
placeholder = COALESCE(${data.placeholder}, placeholder),
help_text = COALESCE(${data.helpText}, help_text)
WHERE id = ${parseInt(id)}
RETURNING *
`);
res.json(result.rows[0]);
} catch (error) {
console.error("Error updating field:", error);
res.status(500).json({ error: "Failed to update field" });
}
});
router.delete("/fields/:id", async (req, res) => {
try {
const { id } = req.params;
await db.execute(sql`DELETE FROM arc_fields WHERE id = ${parseInt(id)}`);
res.json({ success: true });
} catch (error) {
console.error("Error deleting field:", error);
res.status(500).json({ error: "Failed to delete field" });
}
});
router.get("/doctypes/:name/schema", async (req, res) => {
try {
const { name } = req.params;
const doctypeResult = await db.execute(sql`
SELECT * FROM arc_doctypes WHERE name = ${name} AND status = 'active'
`);
if (doctypeResult.rows.length === 0) {
return res.status(404).json({ error: "DocType not found" });
}
const doctype = doctypeResult.rows[0] as any;
const fieldsResult = await db.execute(sql`
SELECT * FROM arc_fields
WHERE doctype_id = ${doctype.id}
ORDER BY sort_order, id
`);
res.json({
doctype,
fields: fieldsResult.rows
});
} catch (error) {
console.error("Error fetching schema:", error);
res.status(500).json({ error: "Failed to fetch schema" });
}
});
router.get("/pages", async (req, res) => {
try {
const result = await db.execute(sql`
SELECT p.*, d.label as doctype_label
FROM arc_pages p
LEFT JOIN arc_doctypes d ON p.doctype_id = d.id
WHERE p.status = 'active'
ORDER BY p.module, p.title
`);
res.json(result.rows);
} catch (error) {
console.error("Error fetching pages:", error);
res.status(500).json({ error: "Failed to fetch pages" });
}
});
router.post("/pages", async (req, res) => {
try {
const { name, title, route, pageType, docTypeId, icon, module, layout } = req.body;
const result = await db.execute(sql`
INSERT INTO arc_pages (name, title, route, page_type, doctype_id, icon, module, layout)
VALUES (${name}, ${title}, ${route}, ${pageType || 'page'}, ${docTypeId || null}, ${icon || null}, ${module || null}, ${layout ? JSON.stringify(layout) : null})
RETURNING *
`);
res.json(result.rows[0]);
} catch (error) {
console.error("Error creating page:", error);
res.status(500).json({ error: "Failed to create page" });
}
});
router.get("/widgets", async (req, res) => {
try {
const result = await db.execute(sql`
SELECT * FROM arc_widgets WHERE status = 'active' ORDER BY category, label
`);
res.json(result.rows);
} catch (error) {
console.error("Error fetching widgets:", error);
res.status(500).json({ error: "Failed to fetch widgets" });
}
});
router.get("/modules", async (req, res) => {
try {
const result = await db.execute(sql`
SELECT DISTINCT module FROM arc_doctypes
WHERE module IS NOT NULL AND status = 'active'
ORDER BY module
`);
res.json(result.rows.map((r: any) => r.module));
} catch (error) {
console.error("Error fetching modules:", error);
res.status(500).json({ error: "Failed to fetch modules" });
}
});
const toCamelCase = (row: any) => ({
id: row.id,
name: row.name,
description: row.description,
nodes: row.nodes || [],
status: row.status,
createdBy: row.created_by,
createdAt: row.created_at,
updatedAt: row.updated_at
});
router.get("/workflows", async (req, res) => {
try {
const result = await db.execute(sql`
SELECT * FROM arc_workflows WHERE status != 'deleted' ORDER BY created_at DESC
`);
res.json(result.rows.map(toCamelCase));
} catch (error) {
console.error("Error fetching workflows:", error);
res.status(500).json({ error: "Failed to fetch workflows" });
}
});
router.post("/workflows", async (req, res) => {
try {
const { name, description, nodes, status } = req.body;
const result = await db.execute(sql`
INSERT INTO arc_workflows (name, description, nodes, status, created_at, updated_at)
VALUES (${name}, ${description}, ${JSON.stringify(nodes || [])}, ${status || 'draft'}, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
RETURNING *
`);
res.json(toCamelCase(result.rows[0]));
} catch (error) {
console.error("Error creating workflow:", error);
res.status(500).json({ error: "Failed to create workflow" });
}
});
router.put("/workflows/:id", async (req, res) => {
try {
const { id } = req.params;
const { name, description, nodes, status } = req.body;
const result = await db.execute(sql`
UPDATE arc_workflows SET
name = COALESCE(${name}, name),
description = COALESCE(${description}, description),
nodes = COALESCE(${nodes ? JSON.stringify(nodes) : null}, nodes),
status = COALESCE(${status}, status),
updated_at = CURRENT_TIMESTAMP
WHERE id = ${parseInt(id)}
RETURNING *
`);
res.json(toCamelCase(result.rows[0]));
} catch (error) {
console.error("Error updating workflow:", error);
res.status(500).json({ error: "Failed to update workflow" });
}
});
router.delete("/workflows/:id", async (req, res) => {
try {
const { id } = req.params;
await db.execute(sql`
UPDATE arc_workflows SET status = 'deleted' WHERE id = ${parseInt(id)}
`);
res.json({ success: true });
} catch (error) {
console.error("Error deleting workflow:", error);
res.status(500).json({ error: "Failed to delete workflow" });
}
});
export default router;