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:
Haqeem Solehan
2025-10-16 16:05:39 +08:00
commit b124ff8092
336 changed files with 94392 additions and 0 deletions

View 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",
};
}
});

View 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",
};
}
});

View 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",
};
}
});

View 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"
]
}

View 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;
}
}

View 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;
}
}

View 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;
}
}

View 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());
}

View 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}`));
}
});
});
}