Update various configuration files, components, and assets; enhance notification system and API endpoints; improve documentation and styles across the application.
This commit is contained in:
36
server/api/devtool/orm/data/get.get.js
Normal file
36
server/api/devtool/orm/data/get.get.js
Normal file
@@ -0,0 +1,36 @@
|
||||
export default defineEventHandler(async (event) => {
|
||||
try {
|
||||
const { tableName } = getQuery(event);
|
||||
|
||||
if (!tableName) {
|
||||
return {
|
||||
statusCode: 400,
|
||||
message: "Table name is required",
|
||||
};
|
||||
}
|
||||
|
||||
// const JSONSchemaTable = getPrismaSchemaTable(tableName);
|
||||
// console.log(JSONSchemaTable);
|
||||
|
||||
const getData = await prisma.$queryRawUnsafe(`SELECT * FROM ${tableName}`);
|
||||
|
||||
if (getData.length === 0) {
|
||||
return {
|
||||
statusCode: 404,
|
||||
message: "Data not found",
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
message: "Data successfully fetched",
|
||||
data: getData,
|
||||
};
|
||||
} catch (error) {
|
||||
console.log(error.message);
|
||||
return {
|
||||
statusCode: 500,
|
||||
message: "Internal Server Error",
|
||||
};
|
||||
}
|
||||
});
|
||||
37
server/api/devtool/orm/schema.get.js
Normal file
37
server/api/devtool/orm/schema.get.js
Normal file
@@ -0,0 +1,37 @@
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
try {
|
||||
const { type } = getQuery(event);
|
||||
|
||||
if (!type) {
|
||||
return {
|
||||
statusCode: 400,
|
||||
message: "Type is required",
|
||||
};
|
||||
}
|
||||
|
||||
if (type !== "table" && type !== "field") {
|
||||
return {
|
||||
statusCode: 400,
|
||||
message: "Invalid type",
|
||||
};
|
||||
}
|
||||
|
||||
let schema = null;
|
||||
if (type == "table") schema = getPrismaSchemaTable();
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
message: "Schema successfully fetched",
|
||||
data: schema,
|
||||
};
|
||||
} catch (error) {
|
||||
console.log(error.message);
|
||||
return {
|
||||
statusCode: 500,
|
||||
message: "Internal Server Error",
|
||||
};
|
||||
}
|
||||
});
|
||||
34
server/api/devtool/orm/studio.get.js
Normal file
34
server/api/devtool/orm/studio.get.js
Normal file
@@ -0,0 +1,34 @@
|
||||
import { exec } from "node:child_process";
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
try {
|
||||
let error = false;
|
||||
|
||||
// Run command yarn prisma studio
|
||||
exec("npx prisma studio", (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
console.error(`exec error: ${error}`);
|
||||
error = true;
|
||||
}
|
||||
console.log(`stdout: ${stdout}`);
|
||||
console.error(`stderr: ${stderr}`);
|
||||
});
|
||||
|
||||
if (error)
|
||||
return {
|
||||
statusCode: 500,
|
||||
message: "Internal Server Error",
|
||||
};
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
message: "Prisma Studio successfully launched",
|
||||
};
|
||||
} catch (error) {
|
||||
console.log(error.message);
|
||||
return {
|
||||
statusCode: 500,
|
||||
message: "Internal Server Error",
|
||||
};
|
||||
}
|
||||
});
|
||||
106
server/api/devtool/orm/table/config/configuration.json
Normal file
106
server/api/devtool/orm/table/config/configuration.json
Normal file
@@ -0,0 +1,106 @@
|
||||
{
|
||||
"columnTypes": [
|
||||
{
|
||||
"group": "Numbers",
|
||||
"options": [
|
||||
"TINYINT",
|
||||
"SMALLINT",
|
||||
"MEDIUMINT",
|
||||
"INT",
|
||||
"BIGINT",
|
||||
"DECIMAL",
|
||||
"FLOAT",
|
||||
"DOUBLE"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Date and Time",
|
||||
"options": ["DATE", "TIME", "DATETIME", "TIMESTAMP", "YEAR"]
|
||||
},
|
||||
{
|
||||
"group": "Strings",
|
||||
"options": [
|
||||
"CHAR",
|
||||
"VARCHAR",
|
||||
"TINYTEXT",
|
||||
"TEXT",
|
||||
"MEDIUMTEXT",
|
||||
"LONGTEXT",
|
||||
"JSON"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Lists",
|
||||
"options": ["ENUM", "SET"]
|
||||
},
|
||||
{
|
||||
"group": "Binary",
|
||||
"options": [
|
||||
"BIT",
|
||||
"BINARY",
|
||||
"VARBINARY",
|
||||
"TINYBLOB",
|
||||
"BLOB",
|
||||
"MEDIUMBLOB",
|
||||
"LONGBLOB"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Geometry",
|
||||
"options": [
|
||||
"GEOMETRY",
|
||||
"POINT",
|
||||
"LINESTRING",
|
||||
"POLYGON",
|
||||
"MULTIPOINT",
|
||||
"MULTILINESTRING",
|
||||
"MULTIPOLYGON",
|
||||
"GEOMETRYCOLLECTION"
|
||||
]
|
||||
}
|
||||
],
|
||||
"dataTypes": [
|
||||
"",
|
||||
"INT",
|
||||
"TINYINT",
|
||||
"SMALLINT",
|
||||
"MEDIUMINT",
|
||||
"BIGINT",
|
||||
"DECIMAL",
|
||||
"NUMERIC",
|
||||
"FLOAT",
|
||||
"DOUBLE",
|
||||
"CHAR",
|
||||
"VARCHAR",
|
||||
"TEXT",
|
||||
"ENUM",
|
||||
"SET",
|
||||
"BINARY",
|
||||
"VARBINARY",
|
||||
"BLOB",
|
||||
"DATE",
|
||||
"TIME",
|
||||
"DATETIME",
|
||||
"TIMESTAMP",
|
||||
"YEAR",
|
||||
"BOOL",
|
||||
"BOOLEAN",
|
||||
"JSON",
|
||||
"JSONB",
|
||||
"XML",
|
||||
"UUID",
|
||||
"GEOMETRY",
|
||||
"POINT",
|
||||
"LINESTRING",
|
||||
"POLYGON"
|
||||
],
|
||||
"tableField": [
|
||||
"name",
|
||||
"type",
|
||||
"length",
|
||||
"defaultValue",
|
||||
"nullable",
|
||||
"primaryKey",
|
||||
"actions"
|
||||
]
|
||||
}
|
||||
81
server/api/devtool/orm/table/config/index.get.js
Normal file
81
server/api/devtool/orm/table/config/index.get.js
Normal file
@@ -0,0 +1,81 @@
|
||||
import fileConfig from "./configuration.json";
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
try {
|
||||
// read configuration file if it exists and return error if it doesn't
|
||||
if (!fileConfig) {
|
||||
return {
|
||||
statusCode: 404,
|
||||
message: "Configuration file not found",
|
||||
};
|
||||
}
|
||||
|
||||
// Get all tables with primary key
|
||||
const tables = await getAllTableWithPK();
|
||||
if (!tables) {
|
||||
return {
|
||||
statusCode: 500,
|
||||
message: "Please check your database connection",
|
||||
};
|
||||
}
|
||||
|
||||
// Remove columnTypes [{"group": "Foreign Keys", "options": [{"label": "TABLE_NAME (COLUMN_NAME)", "value": "TABLE_NAME"}]}] from fileconfig before appending
|
||||
fileConfig.columnTypes = fileConfig.columnTypes.filter(
|
||||
(columnType) => columnType.group !== "Foreign Keys"
|
||||
);
|
||||
|
||||
// Append columnTypes from fileconfig with tables
|
||||
fileConfig.columnTypes.push({
|
||||
...tables,
|
||||
});
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
message: "Configuration file successfully loaded",
|
||||
data: fileConfig,
|
||||
};
|
||||
} catch (error) {
|
||||
console.log(error.message);
|
||||
return {
|
||||
statusCode: 500,
|
||||
message: "Internal Server Error",
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
async function getAllTableWithPK() {
|
||||
try {
|
||||
const tables = await prisma.$queryRaw` SELECT
|
||||
table_name,
|
||||
column_name
|
||||
FROM
|
||||
information_schema.columns
|
||||
WHERE table_schema = DATABASE()
|
||||
AND column_key = 'PRI'`;
|
||||
|
||||
if (!tables) return false;
|
||||
|
||||
// Reformat to {group: "table_name", options: [{label: "TABLE_NAME (COLUMN_NAME)", value: "TABLE_NAME"}]}
|
||||
const remapTables = tables.reduce((acc, table) => {
|
||||
const group = "Foreign Keys";
|
||||
const option = {
|
||||
label: `${table.TABLE_NAME} (${table.COLUMN_NAME})`,
|
||||
value: `[[${table.TABLE_NAME}]]`,
|
||||
};
|
||||
const existingGroup = acc.find((item) => item.group === group);
|
||||
|
||||
if (existingGroup) {
|
||||
existingGroup.options.push(option);
|
||||
} else {
|
||||
acc.push({ group, options: [option] });
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
return remapTables[0];
|
||||
} catch (error) {
|
||||
console.log(error.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
171
server/api/devtool/orm/table/create/index.post.js
Normal file
171
server/api/devtool/orm/table/create/index.post.js
Normal file
@@ -0,0 +1,171 @@
|
||||
import { spawn } from "node:child_process";
|
||||
import { fileURLToPath } from "url";
|
||||
import { dirname, resolve } from "path";
|
||||
import os from "os";
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
try {
|
||||
const { tableName, tableSchema, autoIncrementColumn } =
|
||||
await readBody(event);
|
||||
|
||||
if (!tableName || !tableSchema) {
|
||||
return {
|
||||
statusCode: 400,
|
||||
message: "Bad Request",
|
||||
};
|
||||
}
|
||||
|
||||
// Create Table
|
||||
const isTableCreated = await createTable(
|
||||
tableName,
|
||||
tableSchema,
|
||||
autoIncrementColumn
|
||||
);
|
||||
|
||||
if (isTableCreated.statusCode !== 200)
|
||||
return {
|
||||
statusCode: 500,
|
||||
message: isTableCreated.message,
|
||||
};
|
||||
|
||||
// Run Prisma Command
|
||||
const isPrismaCommandRun = await runPrismaCommand();
|
||||
if (!isPrismaCommandRun)
|
||||
return {
|
||||
statusCode: 500,
|
||||
message: "Prisma Command Failed",
|
||||
};
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
message: "Table Created",
|
||||
};
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return {
|
||||
statusCode: 500,
|
||||
message: "Internal Server Error",
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
async function createTable(tableName, tableSchema) {
|
||||
try {
|
||||
let rawSchema = ``;
|
||||
for (let i = 0; i < tableSchema.length; i++) {
|
||||
const column = tableSchema[i];
|
||||
|
||||
// Sanitize rawSchema
|
||||
if (column.type.includes("[[") && column.type.includes("]]")) {
|
||||
const FKTableName = column.type.replace("[[", "").replace("]]", "");
|
||||
const primaryKey = await prisma.$queryRawUnsafe(
|
||||
"SHOW COLUMNS from " + FKTableName + " where `Key` = 'PRI'"
|
||||
);
|
||||
|
||||
rawSchema += `${column.name} INT NOT NULL, FOREIGN KEY (${column.name}) REFERENCES ${FKTableName}(${primaryKey[0].Field})`;
|
||||
} else {
|
||||
rawSchema += `${column.name}
|
||||
${column.type}${column.length ? "(" + column.length + ")" : ""}
|
||||
${column.defaultValue ? " DEFAULT " + column.defaultValue : ""}
|
||||
${column.nullable ? " NULL" : " NOT NULL "}
|
||||
${column.primaryKey ? " PRIMARY KEY AUTO_INCREMENT" : ""}`;
|
||||
}
|
||||
|
||||
if (i < tableSchema.length - 1) rawSchema += ", ";
|
||||
}
|
||||
|
||||
const sqlStatement = `CREATE TABLE ${tableName} (${rawSchema})`;
|
||||
console.log(sqlStatement);
|
||||
|
||||
const createTable = await prisma.$queryRawUnsafe(sqlStatement);
|
||||
if (!createTable)
|
||||
return {
|
||||
statusCode: 500,
|
||||
message: "Table Creation Failed",
|
||||
};
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
message: "Table Created",
|
||||
};
|
||||
} catch (error) {
|
||||
console.log(error.message);
|
||||
|
||||
// Get Message
|
||||
if (error.message.includes("already exists")) {
|
||||
return {
|
||||
statusCode: 500,
|
||||
message: `Table '${tableName}' already exists!`,
|
||||
};
|
||||
}
|
||||
|
||||
if (error.message.includes("1064")) {
|
||||
return {
|
||||
statusCode: 500,
|
||||
message: "Please ensure the SQL syntax is correct!",
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
statusCode: 500,
|
||||
message: "Table Creation Failed",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function runPrismaCommand() {
|
||||
try {
|
||||
console.log("---------- Run Prisma Command ----------");
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const directory = resolve(__dirname, "../..");
|
||||
|
||||
// Command to execute
|
||||
const command = "npx prisma db pull && npx prisma generate";
|
||||
|
||||
// Determine the appropriate shell command based on the platform
|
||||
let shellCommand;
|
||||
let spawnOptions;
|
||||
switch (os.platform()) {
|
||||
case "win32":
|
||||
shellCommand = `Start-Process cmd -ArgumentList '/c cd "${directory}" && ${command}' -Verb RunAs`;
|
||||
spawnOptions = {
|
||||
shell: "powershell.exe",
|
||||
args: ["-Command", shellCommand],
|
||||
};
|
||||
break;
|
||||
case "darwin":
|
||||
case "linux":
|
||||
shellCommand = `cd "${directory}" && ${command}`;
|
||||
spawnOptions = {
|
||||
shell: "sh",
|
||||
args: ["-c", shellCommand],
|
||||
};
|
||||
break;
|
||||
default:
|
||||
console.error("Unsupported platform:", os.platform());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Spawn child process using the appropriate shell command
|
||||
const childProcess = spawn(spawnOptions.shell, spawnOptions.args, {
|
||||
stdio: "inherit",
|
||||
});
|
||||
|
||||
// Listen for child process events
|
||||
return new Promise((resolve, reject) => {
|
||||
childProcess.on("close", (code) => {
|
||||
if (code === 0) {
|
||||
console.log("Prisma commands executed successfully");
|
||||
resolve(true);
|
||||
} else {
|
||||
console.error(`Child process exited with code ${code}`);
|
||||
reject(new Error(`Child process exited with code ${code}`));
|
||||
}
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error running Prisma commands:", error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
90
server/api/devtool/orm/table/delete/[table]/index.delete.js
Normal file
90
server/api/devtool/orm/table/delete/[table]/index.delete.js
Normal file
@@ -0,0 +1,90 @@
|
||||
import { spawn } from "node:child_process";
|
||||
import { fileURLToPath } from "url";
|
||||
import { dirname, resolve } from "path";
|
||||
import os from "os";
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const tableName = event.context.params.table;
|
||||
|
||||
try {
|
||||
// Drop the table
|
||||
await prisma.$executeRawUnsafe(`DROP TABLE IF EXISTS ${tableName}`);
|
||||
|
||||
// Run Prisma Command to update the schema
|
||||
const isPrismaCommandRun = await runPrismaCommand();
|
||||
if (!isPrismaCommandRun) {
|
||||
return {
|
||||
statusCode: 500,
|
||||
message: "Prisma Command Failed after table deletion",
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
message: `Table '${tableName}' has been successfully deleted.`,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error deleting table:", error);
|
||||
return {
|
||||
statusCode: 500,
|
||||
message: `Failed to delete table '${tableName}'. Error: ${error.message}`,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
async function runPrismaCommand() {
|
||||
try {
|
||||
console.log("---------- Run Prisma Command ----------");
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const directory = resolve(__dirname, "../..");
|
||||
|
||||
// Command to execute
|
||||
const command = "npx prisma db pull && npx prisma generate";
|
||||
|
||||
// Determine the appropriate shell command based on the platform
|
||||
let shellCommand;
|
||||
let spawnOptions;
|
||||
switch (os.platform()) {
|
||||
case "win32":
|
||||
shellCommand = `Start-Process cmd -ArgumentList '/c cd "${directory}" && ${command}' -Verb RunAs`;
|
||||
spawnOptions = {
|
||||
shell: "powershell.exe",
|
||||
args: ["-Command", shellCommand],
|
||||
};
|
||||
break;
|
||||
case "darwin":
|
||||
case "linux":
|
||||
shellCommand = `cd "${directory}" && ${command}`;
|
||||
spawnOptions = {
|
||||
shell: "sh",
|
||||
args: ["-c", shellCommand],
|
||||
};
|
||||
break;
|
||||
default:
|
||||
console.error("Unsupported platform:", os.platform());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Spawn child process using the appropriate shell command
|
||||
const childProcess = spawn(spawnOptions.shell, spawnOptions.args, {
|
||||
stdio: "inherit",
|
||||
});
|
||||
|
||||
// Listen for child process events
|
||||
return new Promise((resolve, reject) => {
|
||||
childProcess.on("close", (code) => {
|
||||
if (code === 0) {
|
||||
console.log("Prisma commands executed successfully");
|
||||
resolve(true);
|
||||
} else {
|
||||
console.error(`Child process exited with code ${code}`);
|
||||
reject(new Error(`Child process exited with code ${code}`));
|
||||
}
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error running Prisma commands:", error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
113
server/api/devtool/orm/table/modify/get.get.js
Normal file
113
server/api/devtool/orm/table/modify/get.get.js
Normal file
@@ -0,0 +1,113 @@
|
||||
export default defineEventHandler(async (event) => {
|
||||
try {
|
||||
const { tableName } = getQuery(event);
|
||||
|
||||
if (!tableName) {
|
||||
return {
|
||||
statusCode: 400,
|
||||
message: "Table name is required",
|
||||
};
|
||||
}
|
||||
|
||||
const result = await prisma.$queryRaw`SELECT DATABASE() AS db_name`;
|
||||
// console.log(result[0].db_name);
|
||||
|
||||
if (result.length === 0) {
|
||||
return {
|
||||
statusCode: 500,
|
||||
message: "Please check your database connection",
|
||||
};
|
||||
}
|
||||
|
||||
let sqlRaw = ` SELECT
|
||||
c.COLUMN_NAME,
|
||||
c.DATA_TYPE,
|
||||
c.CHARACTER_MAXIMUM_LENGTH,
|
||||
c.COLUMN_DEFAULT,
|
||||
c.IS_NULLABLE,
|
||||
c.COLUMN_KEY,
|
||||
kcu.REFERENCED_TABLE_NAME,
|
||||
kcu.REFERENCED_COLUMN_NAME
|
||||
FROM
|
||||
INFORMATION_SCHEMA.COLUMNS c
|
||||
LEFT JOIN
|
||||
INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu ON
|
||||
c.TABLE_SCHEMA = kcu.TABLE_SCHEMA AND
|
||||
c.TABLE_NAME = kcu.TABLE_NAME AND
|
||||
c.COLUMN_NAME = kcu.COLUMN_NAME
|
||||
WHERE
|
||||
c.TABLE_SCHEMA = '${result[0].db_name}' AND
|
||||
c.TABLE_NAME = '${tableName}';`;
|
||||
|
||||
// console.log(sqlRaw);
|
||||
|
||||
const getTableDetails = await prisma.$queryRawUnsafe(sqlRaw);
|
||||
// console.log(getTableDetails);
|
||||
|
||||
/*
|
||||
[{
|
||||
"actions": "",
|
||||
"defaultValue": "",
|
||||
"length": "",
|
||||
"name": "PID",
|
||||
"nullable": "",
|
||||
"primaryKey": true,
|
||||
"type": "INT"
|
||||
},
|
||||
{
|
||||
"actions": "",
|
||||
"defaultValue": "",
|
||||
"length": "",
|
||||
"name": "Pproduct",
|
||||
"nullable": true,
|
||||
"primaryKey": "",
|
||||
"type": "VARCHAR"
|
||||
},
|
||||
{
|
||||
"actions": "",
|
||||
"defaultValue": "",
|
||||
"length": "",
|
||||
"name": "userID",
|
||||
"nullable": "",
|
||||
"primaryKey": "",
|
||||
"type": "[[user]]"
|
||||
}]
|
||||
*/
|
||||
|
||||
let tableDetailsData = [];
|
||||
|
||||
// Loop through the result and convert bigInt to number
|
||||
for (let i = 0; i < getTableDetails.length; i++) {
|
||||
const table = getTableDetails[i];
|
||||
|
||||
tableDetailsData.push({
|
||||
name: table.COLUMN_NAME,
|
||||
type: table.REFERENCED_TABLE_NAME
|
||||
? `[[${table.REFERENCED_TABLE_NAME}]]`
|
||||
: table.DATA_TYPE.toUpperCase(),
|
||||
length: bigIntToNumber(table.CHARACTER_MAXIMUM_LENGTH),
|
||||
defaultValue: table.COLUMN_DEFAULT,
|
||||
nullable: table.IS_NULLABLE === "YES",
|
||||
primaryKey: table.COLUMN_KEY === "PRI",
|
||||
actions: {},
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
message: "Success",
|
||||
data: tableDetailsData,
|
||||
};
|
||||
} catch (error) {
|
||||
console.log(error.message);
|
||||
return {
|
||||
statusCode: 500,
|
||||
message: "Internal Server Error",
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
function bigIntToNumber(bigInt) {
|
||||
if (bigInt === null) return null;
|
||||
return Number(bigInt.toString());
|
||||
}
|
||||
191
server/api/devtool/orm/table/modify/index.post.js
Normal file
191
server/api/devtool/orm/table/modify/index.post.js
Normal file
@@ -0,0 +1,191 @@
|
||||
import { spawn } from "node:child_process";
|
||||
import { fileURLToPath } from "url";
|
||||
import { dirname, resolve } from "path";
|
||||
import os from "os";
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
try {
|
||||
const { tableName, tableSchema, autoIncrementColumn } =
|
||||
await readBody(event);
|
||||
|
||||
if (!tableName || !tableSchema) {
|
||||
return {
|
||||
statusCode: 400,
|
||||
message: "Bad Request",
|
||||
};
|
||||
}
|
||||
|
||||
// Get existing table structure
|
||||
const existingColumns = await prisma.$queryRaw`
|
||||
SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_KEY
|
||||
FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ${tableName}
|
||||
`;
|
||||
|
||||
// Compare and modify table structure
|
||||
for (const column of tableSchema) {
|
||||
const existingColumn = existingColumns.find(
|
||||
(c) => c.COLUMN_NAME === column.name
|
||||
);
|
||||
|
||||
if (existingColumn) {
|
||||
// Modify existing column
|
||||
await modifyColumn(tableName, column, existingColumn);
|
||||
} else {
|
||||
// Add new column
|
||||
await addColumn(tableName, column);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove columns that are not in the new schema
|
||||
for (const existingColumn of existingColumns) {
|
||||
if (!tableSchema.find((c) => c.name === existingColumn.COLUMN_NAME)) {
|
||||
await removeColumn(tableName, existingColumn.COLUMN_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
// Update auto-increment column if necessary
|
||||
if (autoIncrementColumn) {
|
||||
await updateAutoIncrement(tableName, autoIncrementColumn);
|
||||
}
|
||||
|
||||
// Run Prisma Command to update the schema
|
||||
const isPrismaCommandRun = await runPrismaCommand();
|
||||
if (!isPrismaCommandRun) {
|
||||
return {
|
||||
statusCode: 500,
|
||||
message: "Prisma Command Failed",
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
message: "Table modified successfully",
|
||||
};
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return {
|
||||
statusCode: 500,
|
||||
message: "Internal Server Error",
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
async function modifyColumn(tableName, newColumn, existingColumn) {
|
||||
let alterStatement = `ALTER TABLE ${tableName} MODIFY COLUMN ${newColumn.name} ${newColumn.type}`;
|
||||
|
||||
if (newColumn.length) {
|
||||
alterStatement += `(${newColumn.length})`;
|
||||
}
|
||||
|
||||
alterStatement += newColumn.nullable ? " NULL" : " NOT NULL";
|
||||
|
||||
if (newColumn.defaultValue) {
|
||||
alterStatement += ` DEFAULT ${newColumn.defaultValue}`;
|
||||
}
|
||||
|
||||
await prisma.$executeRawUnsafe(alterStatement);
|
||||
}
|
||||
|
||||
async function addColumn(tableName, column) {
|
||||
let alterStatement = `ALTER TABLE ${tableName} ADD COLUMN ${column.name} ${column.type}`;
|
||||
|
||||
if (column.length) {
|
||||
alterStatement += `(${column.length})`;
|
||||
}
|
||||
|
||||
alterStatement += column.nullable ? " NULL" : " NOT NULL";
|
||||
|
||||
if (column.defaultValue) {
|
||||
alterStatement += ` DEFAULT ${column.defaultValue}`;
|
||||
}
|
||||
|
||||
await prisma.$executeRawUnsafe(alterStatement);
|
||||
}
|
||||
|
||||
async function removeColumn(tableName, columnName) {
|
||||
await prisma.$executeRawUnsafe(
|
||||
`ALTER TABLE ${tableName} DROP COLUMN ${columnName}`
|
||||
);
|
||||
}
|
||||
|
||||
async function updateAutoIncrement(tableName, autoIncrementColumn) {
|
||||
await prisma.$executeRawUnsafe(
|
||||
`ALTER TABLE ${tableName} MODIFY ${autoIncrementColumn} INT AUTO_INCREMENT`
|
||||
);
|
||||
}
|
||||
|
||||
async function runPrismaCommand(retries = 3) {
|
||||
try {
|
||||
console.log("---------- Run Prisma Command ----------");
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const directory = resolve(__dirname, "../..");
|
||||
|
||||
// Command to execute
|
||||
const command = "npx prisma db pull && npx prisma generate";
|
||||
|
||||
// Determine the appropriate shell command based on the platform
|
||||
let shellCommand;
|
||||
let spawnOptions;
|
||||
switch (os.platform()) {
|
||||
case "win32":
|
||||
shellCommand = `Start-Process cmd -ArgumentList '/c cd "${directory}" && ${command}' -Verb RunAs`;
|
||||
spawnOptions = {
|
||||
shell: "powershell.exe",
|
||||
args: ["-Command", shellCommand],
|
||||
};
|
||||
break;
|
||||
case "darwin":
|
||||
case "linux":
|
||||
shellCommand = `cd "${directory}" && ${command}`;
|
||||
spawnOptions = {
|
||||
shell: "sh",
|
||||
args: ["-c", shellCommand],
|
||||
};
|
||||
break;
|
||||
default:
|
||||
console.error("Unsupported platform:", os.platform());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Spawn child process using the appropriate shell command
|
||||
const childProcess = spawn(spawnOptions.shell, spawnOptions.args, {
|
||||
stdio: "inherit",
|
||||
});
|
||||
|
||||
// Listen for child process events
|
||||
return new Promise((resolve, reject) => {
|
||||
childProcess.on("close", (code) => {
|
||||
if (code === 0) {
|
||||
console.log("Prisma commands executed successfully");
|
||||
resolve(true);
|
||||
} else {
|
||||
console.error(`Child process exited with code ${code}`);
|
||||
reject(new Error(`Child process exited with code ${code}`));
|
||||
}
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error running Prisma commands:", error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function spawnCommand(command, args, cwd) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const process = spawn(command, args, {
|
||||
cwd,
|
||||
stdio: "inherit",
|
||||
shell: true,
|
||||
});
|
||||
|
||||
process.on("close", (code) => {
|
||||
if (code === 0) {
|
||||
resolve();
|
||||
} else {
|
||||
reject(new Error(`Command failed with exit code ${code}`));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user