Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions express-mongodb/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@
"dotenv": "^16.4.5",
"express": "^4.19.2",
"mongodb": "^6.5.0",
"mongoose": "^8.1.0",
"moongoose": "^0.0.5"
"mongoose": "^8.1.0"
},
"devDependencies": {
"import-local": "^3.1.0",
Expand Down
4 changes: 4 additions & 0 deletions express-mongodb/server.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
const app = require("./app");
const connectDB = require('./db/connect');

const PORT = process.env.PORT || 3000;

// Connect to MongoDB
connectDB(process.env.MONGODB_URL);

app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
5 changes: 3 additions & 2 deletions node-jwt/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ const cors = require("cors");
const app = express();
// app.use(kmiddleware())


var corsOptions = {
origin: "http://localhost:8081"
origin: process.env.CORS_ORIGIN || "http://localhost:8081",
credentials: true,
optionsSuccessStatus: 200
};

const db = require("./models");
Expand Down
1 change: 0 additions & 1 deletion node-jwt/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
"dependencies": {
"@keploy/sdk": "^2.0.10",
"bcrypt": "^5.1.1",
"bcryptjs": "^2.4.3",
"cors": "^2.8.5",
"express": "^4.19.2",
"jest": "^29.7.0",
Expand Down
6 changes: 6 additions & 0 deletions ts-nhost/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
"version": "1.0.0",
"main": "index.js",
"scripts": {
"build": "tsc",
"start": "node dist/app.js",
"dev": "nodemon src/app.ts",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
Expand All @@ -12,6 +15,7 @@
"@nhost/nhost-js": "^3.1.6",
"@types/cors": "^2.8.17",
"axios": "^1.7.2",
"body-parser": "^1.20.2",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.19.2",
Expand All @@ -20,8 +24,10 @@
},
"description": "",
"devDependencies": {
"@types/body-parser": "^1.19.5",
"@types/express": "^4.17.21",
"@types/node": "^20.14.11",
"@types/dotenv": "^8.2.0",
"nodemon": "^3.1.4",
"ts-node": "^10.9.2",
"typescript": "^5.5.3"
Expand Down
92 changes: 80 additions & 12 deletions ts-nhost/src/controllers/todoController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,19 @@ interface DeleteTodoResponse {
errors?: { message: string }[];
}

// Input validation function
const validateTodoInput = (name: string, isCompleted: boolean): boolean => {
return typeof name === 'string' &&
name.trim().length > 0 &&
name.trim().length <= 255 &&
typeof isCompleted === 'boolean';
};

const validateId = (id: string): boolean => {
const numId = parseInt(id);
return !isNaN(numId) && numId > 0;
};

export const createTable = async (req: Request, res: Response) => {
const createTableQuery = {
type: "run_sql",
Expand Down Expand Up @@ -130,17 +143,33 @@ export const getTodos = async (req: Request, res: Response) => {
export const insertTodo = async (req: Request, res: Response) => {
const { name, isCompleted } = req.body;

// Input validation
if (!validateTodoInput(name, isCompleted)) {
return res.status(400).json({
error: "Invalid input. Name must be a non-empty string (max 255 chars) and isCompleted must be a boolean."
});
}

const mutation = `
mutation {
insert_todos_one(object: { name: "${name}", is_completed: ${isCompleted} }) {
mutation InsertTodo($name: String!, $isCompleted: Boolean!) {
insert_todos_one(object: { name: $name, is_completed: $isCompleted }) {
id
}
}
`;

const variables = {
name: name.trim(),
isCompleted
};

try {
const response: AxiosResponse<InsertTodoResponse> = await axios.post(
url,
{ query: mutation },
{
query: mutation,
variables
},
{ headers }
);
if (response.data.errors) {
Expand All @@ -151,24 +180,47 @@ export const insertTodo = async (req: Request, res: Response) => {
id: response.data.data.insert_todos_one.id,
});
} catch (error) {
res.status(400).json({ error: (error as Error).message });
console.error("Error inserting todo:", error);
res.status(400).json({ error: "Failed to insert todo" });
}
};

export const updateTodo = async (req: Request, res: Response) => {
const { id } = req.params;
const { name, isCompleted } = req.body;

// Input validation
if (!validateId(id)) {
return res.status(400).json({ error: "Invalid ID format" });
}

if (!validateTodoInput(name, isCompleted)) {
return res.status(400).json({
error: "Invalid input. Name must be a non-empty string (max 255 chars) and isCompleted must be a boolean."
});
}

const mutation = `
mutation {
update_todos_by_pk(pk_columns: {id: ${id}}, _set: { name: "${name}", is_completed: ${isCompleted} }) {
mutation UpdateTodo($id: Int!, $name: String!, $isCompleted: Boolean!) {
update_todos_by_pk(pk_columns: {id: $id}, _set: { name: $name, is_completed: $isCompleted }) {
id
}
}
`;

const variables = {
id: parseInt(id),
name: name.trim(),
isCompleted
};

try {
const response: AxiosResponse<UpdateTodoResponse> = await axios.post(
url,
{ query: mutation },
{
query: mutation,
variables
},
{ headers }
);
if (response.data.errors) {
Expand All @@ -179,23 +231,38 @@ export const updateTodo = async (req: Request, res: Response) => {
id: response.data.data.update_todos_by_pk.id,
});
} catch (error) {
res.status(400).json({ error: (error as Error).message });
console.error("Error updating todo:", error);
res.status(400).json({ error: "Failed to update todo" });
}
};

export const deleteTodo = async (req: Request, res: Response) => {
const { id } = req.params;

// Input validation
if (!validateId(id)) {
return res.status(400).json({ error: "Invalid ID format" });
}

const mutation = `
mutation {
delete_todos_by_pk(id: ${id}) {
mutation DeleteTodo($id: Int!) {
delete_todos_by_pk(id: $id) {
id
}
}
`;

const variables = {
id: parseInt(id)
};

try {
const response: AxiosResponse<DeleteTodoResponse> = await axios.post(
url,
{ query: mutation },
{
query: mutation,
variables
},
{ headers }
);
if (response.data.errors) {
Expand All @@ -206,6 +273,7 @@ export const deleteTodo = async (req: Request, res: Response) => {
id: response.data.data.delete_todos_by_pk.id,
});
} catch (error) {
res.status(400).json({ error: (error as Error).message });
console.error("Error deleting todo:", error);
res.status(400).json({ error: "Failed to delete todo" });
}
};
48 changes: 42 additions & 6 deletions ts-nhost/src/controllers/userController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,39 @@ interface GraphQLResponse<T> {
errors?: { message: string }[];
}

// Input validation functions
const validateUserInput = (displayName: string, email: string, password: string, locale: string): boolean => {
return typeof displayName === 'string' &&
displayName.trim().length > 0 &&
displayName.trim().length <= 255 &&
typeof email === 'string' &&
email.includes('@') &&
typeof password === 'string' &&
password.length >= 6 &&
typeof locale === 'string' &&
locale.length === 2;
};

const validateId = (id: string): boolean => {
const numId = parseInt(id);
return !isNaN(numId) && numId > 0;
};

export const createUser = async (req: Request, res: Response) => {
const { displayName, email, password, locale }: User = req.body;

// Input validation
if (!validateUserInput(displayName, email, password, locale)) {
return res.status(400).json({
error: "Invalid input. All fields are required. Email must be valid, password must be at least 6 characters, and locale must be 2 characters."
});
}

const user = {
email,
email: email.trim().toLowerCase(),
passwordHash: password,
locale,
displayName,
locale: locale.trim().toLowerCase(),
displayName: displayName.trim(),
};

const query = `
Expand Down Expand Up @@ -95,17 +120,28 @@ export const getAllUsers = async (req: Request, res: Response) => {

export const deleteUser = async (req: Request, res: Response) => {
const { id } = req.params;

// Input validation
if (!validateId(id)) {
return res.status(400).json({ error: "Invalid ID format" });
}

const mutation = `
mutation {
deleteUser(id: "${id}") {
mutation DeleteUser($id: Int!) {
deleteUser(id: $id) {
id
}
}
`;

const variables = {
id: parseInt(id)
};

try {
const response: AxiosResponse<
GraphQLResponse<{ deleteUser: { id: string } }>
> = await axios.post(url, { query: mutation }, { headers });
> = await axios.post(url, { query: mutation, variables }, { headers });

if (response.data.errors) {
throw new Error(response.data.errors[0].message);
Expand Down