Skip to content
Merged
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
11 changes: 10 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,13 @@ PGADMIN_PORT=8080

# JWT Configuration
JWT_SECRET=your-super-secret-jwt-key-change-this-in-production-min-32-chars
JWT_EXPIRES_IN=7d
JWT_EXPIRES_IN=7d

# SMTP Configuration
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your-email@gmail.com
SMTP_PASS=your-app-password
SMTP_SECURE=false
EMAIL_FROM=no-reply@fixpoint.com
EMAIL_FROM_NAME=FixPoint_Support
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,25 @@ npm run dev

The server default route is: `http://localhost:5000/api/`.

## Database Setup & Seeding

If you are running the project for the first time or need to reset your database, follow these steps:

1. **Run Migrations**: Create the necessary tables in the database.
```bash
npm run db:migrate
```

2. **Seed Data**: Populate the database with demo users and branches.
```bash
npm run db:seed
```

**To reset the entire database (Undo all migrations, remigrate, and re-seed):**
```bash
npm run db:reset
Comment on lines +76 to +86
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The README.md references npm scripts (db:migrate, db:seed, db:reset) that don't exist in package.json. These scripts need to be added to package.json, or the README documentation should be updated to use the correct commands (e.g., npx sequelize-cli db:migrate).

Suggested change
npm run db:migrate
```
2. **Seed Data**: Populate the database with demo users and branches.
```bash
npm run db:seed
```
**To reset the entire database (Undo all migrations, remigrate, and re-seed):**
```bash
npm run db:reset
npx sequelize-cli db:migrate
  1. Seed Data: Populate the database with demo users and branches.
    npx sequelize-cli db:seed

To reset the entire database (Undo all migrations, remigrate, and re-seed):

npx sequelize-cli db:migrate:undo:all && npx sequelize-cli db:migrate && npx sequelize-cli db:seed

Copilot uses AI. Check for mistakes.
```

## Health endpoints

The project exposes health endpoints mounted under `/api/health`:
Expand Down
33 changes: 23 additions & 10 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"mathjs": "^15.1.0",
"minio": "^8.0.6",
"multer": "^2.0.2",
"nodemailer": "^7.0.11",
"pg": "^8.16.3",
"pg-hstore": "^2.3.4",
"sequelize": "^6.37.7",
Expand All @@ -41,6 +42,7 @@
"inflight": "npm:@isaacs/inflight@^1.0.6",
"glob": "10.5.0",
"validator": "^13.15.22",
"js-yaml": "3.14.2"
"js-yaml": "3.14.2",
"fast-xml-parser": "5.3.4"
}
}
}
22 changes: 22 additions & 0 deletions src/database/migrations/20251220000000-add-reset-otp-fields.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
'use strict';

module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.addColumn('Users', 'resetOTP', {
type: Sequelize.STRING(10),
allowNull: true,
comment: '6-digit OTP for password reset'
Comment on lines +5 to +8
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The resetOTP column is defined to store the raw 6-digit password reset code in plaintext, which means anyone who can read the database (including via a potential SQL injection elsewhere) can see valid reset codes and hijack accounts before they expire. Instead of persisting the raw OTP value, store only a hashed representation (similar to password hashing) and compare hashes during verification so that a database compromise does not expose usable reset codes.

Copilot uses AI. Check for mistakes.
});

await queryInterface.addColumn('Users', 'resetOTPExpiry', {
type: Sequelize.DATE,
allowNull: true,
comment: 'Expiration time of the reset OTP'
});
},

async down(queryInterface, Sequelize) {
await queryInterface.removeColumn('Users', 'resetOTP');
await queryInterface.removeColumn('Users', 'resetOTPExpiry');
}
};
22 changes: 22 additions & 0 deletions src/database/migrations/20251220165748-add-reset-otp-fields.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
'use strict';

/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up (queryInterface, Sequelize) {
/**
* Add altering commands here.
*
* Example:
* await queryInterface.createTable('users', { id: Sequelize.INTEGER });
*/
},

async down (queryInterface, Sequelize) {
/**
* Add reverting commands here.
*
* Example:
* await queryInterface.dropTable('users');
*/
}
};
Comment on lines +3 to +22
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This migration file is empty and contains only template comments with no actual migration logic. The up and down methods are empty stubs. This appears to be a duplicate of the .cjs migration file (20251220000000-add-reset-otp-fields.cjs) which contains the actual implementation. This empty migration file should be removed to avoid confusion and potential issues during migration execution.

Suggested change
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up (queryInterface, Sequelize) {
/**
* Add altering commands here.
*
* Example:
* await queryInterface.createTable('users', { id: Sequelize.INTEGER });
*/
},
async down (queryInterface, Sequelize) {
/**
* Add reverting commands here.
*
* Example:
* await queryInterface.dropTable('users');
*/
}
};
/**
* This file is intentionally left empty.
*
* The actual "add reset OTP fields" migration is implemented in:
* 20251220000000-add-reset-otp-fields.cjs
*
* This .js stub exists only to prevent confusion; it is not used as a
* Sequelize migration and exports no migration logic.
*/

Copilot uses AI. Check for mistakes.
12 changes: 10 additions & 2 deletions src/middleware/upload.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,26 @@ import multer from 'multer';

// File filter - only allow images
const fileFilter = (req, file, cb) => {
console.log('Multer file filter - Received file:', {
fieldname: file.fieldname,
originalname: file.originalname,
mimetype: file.mimetype,
encoding: file.encoding
});

const allowedMimeTypes = [
'image/jpeg',
'image/jpg',
'image/png',
'image/gif',
'image/webp'
];

if (allowedMimeTypes.includes(file.mimetype)) {
console.log('✅ File type accepted:', file.mimetype);
cb(null, true);
} else {
cb(new Error('Invalid file type. Only JPEG, PNG, GIF, and WebP images are allowed.'), false);
console.log('❌ File type rejected:', file.mimetype);
cb(new Error('Invalid file type. Only JPEG, PNG, and WebP images are allowed.'), false);
}
};

Expand Down
4 changes: 4 additions & 0 deletions src/middleware/validation.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@ import { body, param, validationResult } from 'express-validator';
* Middleware to handle validation errors
*/
export const handleValidationErrors = (req, res, next) => {
console.log('🔍 Validation middleware - Request body:', JSON.stringify(req.body));
console.log('🔍 Validation middleware - Request file:', req.file ? req.file.originalname : 'No file');

const errors = validationResult(req);
if (!errors.isEmpty()) {
console.log('❌ Validation errors:', JSON.stringify(errors.array()));
return res.status(400).json({
success: false,
errors: errors.array().map(err => ({
Expand Down
10 changes: 10 additions & 0 deletions src/models/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,16 @@ const User = sequelize.define(
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: true
},
resetOTP: {
type: DataTypes.STRING(10),
allowNull: true,
comment: '6-digit OTP for password reset'
},
resetOTPExpiry: {
type: DataTypes.DATE,
allowNull: true,
comment: 'Expiration time of the reset OTP'
}
},
{
Expand Down
Loading