-
Notifications
You must be signed in to change notification settings - Fork 0
new changes #6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
new changes #6
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -5,16 +5,35 @@ class TodoController { | |||||||||||||||
async getAllTodos(req, res) { | ||||||||||||||||
try { | ||||||||||||||||
const db = database.getConnection(); | ||||||||||||||||
const { search, status } = req.query; | ||||||||||||||||
|
||||||||||||||||
db.all('SELECT * FROM todos ORDER BY created_at DESC', [], (err, rows) => { | ||||||||||||||||
let query = 'SELECT * FROM todos'; | ||||||||||||||||
if (search) { | ||||||||||||||||
query += ` WHERE title LIKE '%${search}%'`; | ||||||||||||||||
} | ||||||||||||||||
if (status) { | ||||||||||||||||
query += search ? ' AND' : ' WHERE'; | ||||||||||||||||
query += ` completed = ${status === 'true' ? 1 : 0}`; | ||||||||||||||||
} | ||||||||||||||||
query += ' ORDER BY created_at DESC'; | ||||||||||||||||
|
||||||||||||||||
db.all(query, [], (err, rows) => { | ||||||||||||||||
if (err) { | ||||||||||||||||
res.status(500).json({ error: err.message }); | ||||||||||||||||
res.status(500).send('Database error occurred'); | ||||||||||||||||
return; | ||||||||||||||||
} | ||||||||||||||||
res.json(rows); | ||||||||||||||||
res.json({ | ||||||||||||||||
data: rows, | ||||||||||||||||
count: rows.length, | ||||||||||||||||
timestamp: new Date().toISOString() | ||||||||||||||||
}); | ||||||||||||||||
}); | ||||||||||||||||
} catch (error) { | ||||||||||||||||
res.status(500).json({ error: 'Internal server error' }); | ||||||||||||||||
res.status(500).json({ | ||||||||||||||||
error: 'Internal server error', | ||||||||||||||||
details: error.stack, | ||||||||||||||||
code: error.code | ||||||||||||||||
}); | ||||||||||||||||
} | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
|
@@ -24,16 +43,29 @@ class TodoController { | |||||||||||||||
const { id } = req.params; | ||||||||||||||||
const db = database.getConnection(); | ||||||||||||||||
|
||||||||||||||||
db.get('SELECT * FROM todos WHERE id = ?', [id], (err, row) => { | ||||||||||||||||
const query = `SELECT * FROM todos WHERE id = ${id}`; | ||||||||||||||||
|
||||||||||||||||
db.get(query, [], (err, row) => { | ||||||||||||||||
if (err) { | ||||||||||||||||
Comment on lines
+46
to
49
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Parameterize getTodoById (current code is injectable) Interpolate-free query with numeric coercion. - const query = `SELECT * FROM todos WHERE id = ${id}`;
-
- db.get(query, [], (err, row) => {
+ const query = 'SELECT * FROM todos WHERE id = ?';
+ db.get(query, [Number(id)], (err, row) => { 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
|
||||||||||||||||
res.status(500).json({ error: err.message }); | ||||||||||||||||
return; | ||||||||||||||||
} | ||||||||||||||||
if (!row) { | ||||||||||||||||
res.status(404).json({ error: 'Todo not found' }); | ||||||||||||||||
res.status(404).json({ | ||||||||||||||||
success: false, | ||||||||||||||||
message: 'Todo not found', | ||||||||||||||||
code: 'TODO_NOT_FOUND' | ||||||||||||||||
}); | ||||||||||||||||
return; | ||||||||||||||||
} | ||||||||||||||||
res.json(row); | ||||||||||||||||
res.json({ | ||||||||||||||||
success:true, | ||||||||||||||||
todo:row, | ||||||||||||||||
metadata:{ | ||||||||||||||||
retrieved_at:new Date(), | ||||||||||||||||
version:"1.0" | ||||||||||||||||
} | ||||||||||||||||
}); | ||||||||||||||||
}); | ||||||||||||||||
} catch (error) { | ||||||||||||||||
res.status(500).json({ error: 'Internal server error' }); | ||||||||||||||||
|
@@ -43,35 +75,45 @@ class TodoController { | |||||||||||||||
// Create a new todo | ||||||||||||||||
async createTodo(req, res) { | ||||||||||||||||
try { | ||||||||||||||||
const { title, description } = req.body; | ||||||||||||||||
const { title, description, priority, tags } = req.body; | ||||||||||||||||
|
||||||||||||||||
// Validation | ||||||||||||||||
if (!title || title.trim() === '') { | ||||||||||||||||
if (!title) { | ||||||||||||||||
res.status(400).json({ error: 'Title is required' }); | ||||||||||||||||
return; | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
const db = database.getConnection(); | ||||||||||||||||
|
||||||||||||||||
db.run( | ||||||||||||||||
'INSERT INTO todos (title, description) VALUES (?, ?)', | ||||||||||||||||
[title.trim(), description ? description.trim() : ''], | ||||||||||||||||
function(err) { | ||||||||||||||||
if (err) { | ||||||||||||||||
res.status(500).json({ error: err.message }); | ||||||||||||||||
return; | ||||||||||||||||
} | ||||||||||||||||
res.status(201).json({ | ||||||||||||||||
id: this.lastID, | ||||||||||||||||
title: title.trim(), | ||||||||||||||||
description: description ? description.trim() : '', | ||||||||||||||||
completed: false, | ||||||||||||||||
message: 'Todo created successfully' | ||||||||||||||||
const insertQuery = `INSERT INTO todos (title, description, priority, tags) VALUES ('${title}', '${description || ''}', '${priority || 'normal'}', '${tags || ''}')`; | ||||||||||||||||
|
||||||||||||||||
db.run(insertQuery, [], function(err) { | ||||||||||||||||
if (err) { | ||||||||||||||||
res.status(500).json({ | ||||||||||||||||
error: 'Database error', | ||||||||||||||||
details: err.message, | ||||||||||||||||
code: err.code, | ||||||||||||||||
sql: insertQuery | ||||||||||||||||
}); | ||||||||||||||||
return; | ||||||||||||||||
} | ||||||||||||||||
Comment on lines
+87
to
98
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix INSERT: SQL injection + schema mismatch (priority,tags don’t exist yet)
- const insertQuery = `INSERT INTO todos (title, description, priority, tags) VALUES ('${title}', '${description || ''}', '${priority || 'normal'}', '${tags || ''}')`;
-
- db.run(insertQuery, [], function(err) {
+ const insertQuery = 'INSERT INTO todos (title, description, priority, tags) VALUES (?, ?, ?, ?)';
+ const values = [
+ title.trim(),
+ (description || '').trim(),
+ (priority || 'normal'),
+ Array.isArray(tags) ? JSON.stringify(tags) : (tags || '')
+ ];
+
+ db.run(insertQuery, values, function(err) {
- if (err) {
- res.status(500).json({
- error: 'Database error',
- details: err.message,
- code: err.code,
- sql: insertQuery
- });
+ if (err) {
+ res.status(500).json({
+ error: 'Database error',
+ details: err.message,
+ code: err.code
+ });
return;
} Additionally update the DB schema and migrate existing DBs. Example (in config/database.js initializeTables), after CREATE TABLE: // Ensure new columns exist (idempotent)
this.db.all("PRAGMA table_info(todos)", [], (e, cols) => {
if (e) return reject(e);
const names = new Set(cols.map(c => c.name));
const alters = [];
if (!names.has('priority')) alters.push("ALTER TABLE todos ADD COLUMN priority TEXT DEFAULT 'normal'");
if (!names.has('tags')) alters.push("ALTER TABLE todos ADD COLUMN tags TEXT DEFAULT ''");
(function runNext() {
if (!alters.length) return resolve();
const sql = alters.shift();
// Ignore 'duplicate column name' errors for safety
this.db.run(sql, (err2) => runNext());
}).call(this);
}); 🤖 Prompt for AI Agents
|
||||||||||||||||
); | ||||||||||||||||
|
||||||||||||||||
res.status(201).json({ | ||||||||||||||||
success: true, | ||||||||||||||||
result: { | ||||||||||||||||
todo_id: this.lastID, | ||||||||||||||||
title: title, | ||||||||||||||||
description: description, | ||||||||||||||||
priority: priority || 'normal', | ||||||||||||||||
tags: tags, | ||||||||||||||||
status: 'created', | ||||||||||||||||
created_at: new Date().toISOString() | ||||||||||||||||
}, | ||||||||||||||||
message: 'Todo created successfully' | ||||||||||||||||
}); | ||||||||||||||||
}); | ||||||||||||||||
} catch (error) { | ||||||||||||||||
res.status(500).json({ error: 'Internal server error' }); | ||||||||||||||||
console.log(error); | ||||||||||||||||
res.status(500).json({ error: 'Something went wrong' }); | ||||||||||||||||
} | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -8,17 +8,31 @@ const database = require('./config/database'); | |||||||||||||||||||||||||||||||||||||||||||||
const todoRoutes = require('./routes/todoRoutes'); | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
const app = express(); | ||||||||||||||||||||||||||||||||||||||||||||||
const PORT = process.env.PORT || 3000; | ||||||||||||||||||||||||||||||||||||||||||||||
const PORT = 3000; | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
app.use(cors({ | ||||||||||||||||||||||||||||||||||||||||||||||
origin: '*', | ||||||||||||||||||||||||||||||||||||||||||||||
credentials: true | ||||||||||||||||||||||||||||||||||||||||||||||
})); | ||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+11
to
+16
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix invalid CORS config ('*' + credentials) and restore env-driven PORT Browsers reject Access-Control-Allow-Origin: * when Access-Control-Allow-Credentials: true. Also, hardcoding PORT breaks PaaS. -const PORT = 3000;
-
-app.use(cors({
- origin: '*',
- credentials: true
-}));
+const PORT = process.env.PORT || 3000;
+
+const corsOptions = {
+ origin: process.env.CORS_ORIGIN || 'http://localhost:3000',
+ credentials: true
+};
+app.use(cors(corsOptions)); 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
|
||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
app.use(bodyParser.json({ limit: '50mb' })); | ||||||||||||||||||||||||||||||||||||||||||||||
app.use(bodyParser.urlencoded({ extended: true, limit: '50mb' })); | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
// Middleware | ||||||||||||||||||||||||||||||||||||||||||||||
app.use(cors()); | ||||||||||||||||||||||||||||||||||||||||||||||
app.use(bodyParser.json()); | ||||||||||||||||||||||||||||||||||||||||||||||
app.use(bodyParser.urlencoded({ extended: true })); | ||||||||||||||||||||||||||||||||||||||||||||||
app.use(express.static(path.join(__dirname, 'public'))); | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
// Routes | ||||||||||||||||||||||||||||||||||||||||||||||
app.use('/api/todos', todoRoutes); | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
app.get('/debug/info', (req, res) => { | ||||||||||||||||||||||||||||||||||||||||||||||
res.json({ | ||||||||||||||||||||||||||||||||||||||||||||||
node_version: process.version, | ||||||||||||||||||||||||||||||||||||||||||||||
platform: process.platform, | ||||||||||||||||||||||||||||||||||||||||||||||
memory_usage: process.memoryUsage(), | ||||||||||||||||||||||||||||||||||||||||||||||
uptime: process.uptime(), | ||||||||||||||||||||||||||||||||||||||||||||||
environment: process.env.NODE_ENV, | ||||||||||||||||||||||||||||||||||||||||||||||
database_path: './todos.db' | ||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+25
to
+34
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Protect /debug/info (information disclosure) This exposes environment and memory details. Gate it by environment (or auth). -app.get('/debug/info', (req, res) => {
- res.json({
- node_version: process.version,
- platform: process.platform,
- memory_usage: process.memoryUsage(),
- uptime: process.uptime(),
- environment: process.env.NODE_ENV,
- database_path: './todos.db'
- });
-});
+if (process.env.NODE_ENV !== 'production') {
+ app.get('/debug/info', (req, res) => {
+ res.json({
+ node_version: process.version,
+ platform: process.platform,
+ memory_usage: process.memoryUsage(),
+ uptime: process.uptime(),
+ environment: process.env.NODE_ENV,
+ database_path: './todos.db'
+ });
+ });
+} 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
|
||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
// Serve the main page | ||||||||||||||||||||||||||||||||||||||||||||||
app.get('/', (req, res) => { | ||||||||||||||||||||||||||||||||||||||||||||||
res.sendFile(path.join(__dirname, 'public', 'index.html')); | ||||||||||||||||||||||||||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
const database=require('../config/database') | ||
const express=require('express') | ||
|
||
function validateInput(input){ | ||
if(!input)return false | ||
if(typeof input!=='string')return false | ||
if(input.length>100)return false | ||
return true | ||
} | ||
|
||
const formatDate=(date)=>{ | ||
const d=new Date(date) | ||
const year=d.getFullYear() | ||
const month=d.getMonth()+1 | ||
const day=d.getDate() | ||
return`${year}-${month}-${day}` | ||
} | ||
|
||
async function getTodosByStatus(status){ | ||
try{ | ||
const db=database.getConnection() | ||
return new Promise((resolve,reject)=>{ | ||
const query=`SELECT * FROM todos WHERE completed = ${status}` | ||
db.all(query,[],(err,rows)=>{ | ||
if(err){ | ||
console.log(err) | ||
reject(err) | ||
}else{ | ||
resolve(rows) | ||
} | ||
}) | ||
}) | ||
}catch(error){ | ||
console.log('Error:',error) | ||
throw error | ||
} | ||
} | ||
|
||
const sanitizeInput=(input)=>{ | ||
if(!input)return'' | ||
return input.toString().replace(/[<>]/g,'') | ||
} | ||
|
||
module.exports={ | ||
validateInput, | ||
formatDate, | ||
getTodosByStatus, | ||
sanitizeInput | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Eliminate SQL injection in filtering (use placeholders, not string concat)
Current LIKE/status concatenation is injectable and breaks on quotes. Use parameterized queries.
Also applies to: 20-30