From ade694da4c929a4380daad1d70c62d1651faeb6f Mon Sep 17 00:00:00 2001 From: antanst <> Date: Wed, 6 Aug 2025 18:09:27 +0300 Subject: [PATCH] Deduplicate validateTagName function. --- backend/routes/notes.js | 33 +-------------------------------- backend/routes/projects.js | 33 +-------------------------------- backend/routes/tags.js | 33 +-------------------------------- backend/routes/tasks.js | 33 +-------------------------------- backend/utils/validation.js | 34 ++++++++++++++++++++++++++++++++++ 5 files changed, 38 insertions(+), 128 deletions(-) create mode 100644 backend/utils/validation.js diff --git a/backend/routes/notes.js b/backend/routes/notes.js index d700ea3..3afa843 100644 --- a/backend/routes/notes.js +++ b/backend/routes/notes.js @@ -2,40 +2,9 @@ const express = require('express'); const { Note, Tag, Project, sequelize } = require('../models'); const { Op } = require('sequelize'); const { extractUidFromSlug } = require('../utils/slug-utils'); +const { validateTagName } = require('../utils/validation'); const router = express.Router(); -// Helper function to validate tag name (same as in tags.js) -function validateTagName(name) { - if (!name || !name.trim()) { - return { valid: false, error: 'Tag name is required' }; - } - - const trimmedName = name.trim(); - - // Check for invalid characters that can break URLs or cause issues - const invalidChars = /[#%&{}\\<>*?/$!'":@+`|=]/; - if (invalidChars.test(trimmedName)) { - return { - valid: false, - error: 'Tag name contains invalid characters. Please avoid: # % & { } \\ < > * ? / $ ! \' " : @ + ` | =', - }; - } - - // Check length limits - if (trimmedName.length > 50) { - return { - valid: false, - error: 'Tag name must be 50 characters or less', - }; - } - - if (trimmedName.length < 1) { - return { valid: false, error: 'Tag name cannot be empty' }; - } - - return { valid: true, name: trimmedName }; -} - // Helper function to update note tags async function updateNoteTags(note, tagsArray, userId) { if (!tagsArray || tagsArray.length === 0) { diff --git a/backend/routes/projects.js b/backend/routes/projects.js index bd73bf2..339bd70 100644 --- a/backend/routes/projects.js +++ b/backend/routes/projects.js @@ -7,40 +7,9 @@ const fs = require('fs'); const { Project, Task, Tag, Area, Note, sequelize } = require('../models'); const { Op } = require('sequelize'); const { extractUidFromSlug } = require('../utils/slug-utils'); +const { validateTagName } = require('../utils/validation'); const router = express.Router(); -// Helper function to validate tag name (same as in tags.js) -function validateTagName(name) { - if (!name || !name.trim()) { - return { valid: false, error: 'Tag name is required' }; - } - - const trimmedName = name.trim(); - - // Check for invalid characters that can break URLs or cause issues - const invalidChars = /[#%&{}\\<>*?/$!'":@+`|=]/; - if (invalidChars.test(trimmedName)) { - return { - valid: false, - error: 'Tag name contains invalid characters. Please avoid: # % & { } \\ < > * ? / $ ! \' " : @ + ` | =', - }; - } - - // Check length limits - if (trimmedName.length > 50) { - return { - valid: false, - error: 'Tag name must be 50 characters or less', - }; - } - - if (trimmedName.length < 1) { - return { valid: false, error: 'Tag name cannot be empty' }; - } - - return { valid: true, name: trimmedName }; -} - // Helper function to safely format dates const formatDate = (date) => { if (!date) return null; diff --git a/backend/routes/tags.js b/backend/routes/tags.js index be7e547..ed17754 100644 --- a/backend/routes/tags.js +++ b/backend/routes/tags.js @@ -1,41 +1,10 @@ const express = require('express'); const { Tag, Task, Note, Project, sequelize } = require('../models'); const { extractUidFromSlug } = require('../utils/slug-utils'); +const { validateTagName } = require('../utils/validation'); const router = express.Router(); const _ = require('lodash'); -// Helper function to validate tag name -function validateTagName(name) { - if (!name || !name.trim()) { - return { valid: false, error: 'Tag name is required' }; - } - - const trimmedName = name.trim(); - - // Check for invalid characters that can break URLs or cause issues - const invalidChars = /[#%&{}\\<>*?/$!'":@+`|=]/; - if (invalidChars.test(trimmedName)) { - return { - valid: false, - error: 'Tag name contains invalid characters. Please avoid: # % & { } \\ < > * ? / $ ! \' " : @ + ` | =', - }; - } - - // Check length limits - if (trimmedName.length > 50) { - return { - valid: false, - error: 'Tag name must be 50 characters or less', - }; - } - - if (trimmedName.length < 1) { - return { valid: false, error: 'Tag name cannot be empty' }; - } - - return { valid: true, name: trimmedName }; -} - // GET /api/tags router.get('/tags', async (req, res) => { try { diff --git a/backend/routes/tasks.js b/backend/routes/tasks.js index 72890d3..ec74a10 100644 --- a/backend/routes/tasks.js +++ b/backend/routes/tasks.js @@ -3,42 +3,11 @@ const { Task, Tag, Project, TaskEvent, sequelize } = require('../models'); const { Op } = require('sequelize'); const RecurringTaskService = require('../services/recurringTaskService'); const TaskEventService = require('../services/taskEventService'); +const { validateTagName } = require('../utils/validation'); const moment = require('moment-timezone'); const _ = require('lodash'); const router = express.Router(); -// Helper function to validate tag name (same as in tags.js) -function validateTagName(name) { - if (!name || !name.trim()) { - return { valid: false, error: 'Tag name is required' }; - } - - const trimmedName = name.trim(); - - // Check for invalid characters that can break URLs or cause issues - const invalidChars = /[#%&{}\\<>*?/$!'":@+`|=]/; - if (invalidChars.test(trimmedName)) { - return { - valid: false, - error: 'Tag name contains invalid characters. Please avoid: # % & { } \\ < > * ? / $ ! \' " : @ + ` | =', - }; - } - - // Check length limits - if (trimmedName.length > 50) { - return { - valid: false, - error: 'Tag name must be 50 characters or less', - }; - } - - if (trimmedName.length < 1) { - return { valid: false, error: 'Tag name cannot be empty' }; - } - - return { valid: true, name: trimmedName }; -} - // Helper function to serialize task with today move count async function serializeTask(task) { const taskJson = task.toJSON(); diff --git a/backend/utils/validation.js b/backend/utils/validation.js new file mode 100644 index 0000000..608c2d9 --- /dev/null +++ b/backend/utils/validation.js @@ -0,0 +1,34 @@ +function validateTagName(name) { + if (!name || !name.trim()) { + return { valid: false, error: 'Tag name is required' }; + } + + const trimmedName = name.trim(); + + // Check for invalid characters that can break URLs or cause issues + const invalidChars = /[#%&{}\\<>*?/$!'":@+`|=]/; + if (invalidChars.test(trimmedName)) { + return { + valid: false, + error: 'Tag name contains invalid characters. Please avoid: # % & { } \\ < > * ? / $ ! \' " : @ + ` | =', + }; + } + + // Check length limits + if (trimmedName.length > 50) { + return { + valid: false, + error: 'Tag name must be 50 characters or less', + }; + } + + if (trimmedName.length < 1) { + return { valid: false, error: 'Tag name cannot be empty' }; + } + + return { valid: true, name: trimmedName }; +} + +module.exports = { + validateTagName, +};