fixed project slug uniqueness issue
This commit is contained in:
parent
91c24165bf
commit
a9a2866b23
@ -34,6 +34,7 @@ const errors = {
|
||||
revokingOwnPermission: `You can't revoke your own owner permission`,
|
||||
deletingHimself: `You can't delete yourself`,
|
||||
emailRequired: 'Email is required',
|
||||
slugAlreadyExists: 'This slug is already in use by another project',
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
@ -43,9 +43,47 @@ module.exports = class ProjectsService {
|
||||
return uniqueSlug;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate slug uniqueness before create/update
|
||||
* @param {string} slug - Slug to validate
|
||||
* @param {string|null} excludeId - Project ID to exclude (for updates)
|
||||
* @param {Transaction} transaction - DB transaction
|
||||
* @throws {ValidationError} if slug already exists
|
||||
* @returns {string} Normalized slug
|
||||
*/
|
||||
static async validateSlugUniqueness(slug, excludeId, transaction) {
|
||||
const normalizedSlug = ProjectsService.normalizeSlug(slug);
|
||||
|
||||
const whereClause = { slug: normalizedSlug };
|
||||
if (excludeId) {
|
||||
whereClause.id = { [db.Sequelize.Op.ne]: excludeId };
|
||||
}
|
||||
|
||||
const existing = await db.projects.findOne({
|
||||
where: whereClause,
|
||||
paranoid: false,
|
||||
transaction,
|
||||
});
|
||||
|
||||
if (existing) {
|
||||
throw new ValidationError('iam.errors.slugAlreadyExists');
|
||||
}
|
||||
|
||||
return normalizedSlug;
|
||||
}
|
||||
|
||||
static async create(data, currentUser) {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
try {
|
||||
// Validate slug uniqueness if provided
|
||||
if (data.slug) {
|
||||
data.slug = await ProjectsService.validateSlugUniqueness(
|
||||
data.slug,
|
||||
null,
|
||||
transaction,
|
||||
);
|
||||
}
|
||||
|
||||
const createdProject = await ProjectsDBApi.create(data, {
|
||||
currentUser,
|
||||
transaction,
|
||||
@ -196,6 +234,15 @@ module.exports = class ProjectsService {
|
||||
throw new ValidationError('projectsNotFound');
|
||||
}
|
||||
|
||||
// Validate slug uniqueness if slug is being changed
|
||||
if (data.slug && data.slug !== projects.slug) {
|
||||
data.slug = await ProjectsService.validateSlugUniqueness(
|
||||
data.slug,
|
||||
id,
|
||||
transaction,
|
||||
);
|
||||
}
|
||||
|
||||
const updatedProjects = await ProjectsDBApi.update(id, data, {
|
||||
currentUser,
|
||||
transaction,
|
||||
|
||||
@ -24,6 +24,7 @@ import BaseButton from '../../components/BaseButton';
|
||||
import { deleteItem, update, fetch } from '../../stores/projects/projectsSlice';
|
||||
import { useAppDispatch, useAppSelector } from '../../stores/hooks';
|
||||
import { useRouter } from 'next/router';
|
||||
import { toast, ToastContainer } from 'react-toastify';
|
||||
import type { Project } from '../../types/entities';
|
||||
import { logger } from '../../lib/logger';
|
||||
|
||||
@ -143,8 +144,22 @@ const EditProjectsPage = () => {
|
||||
og_image_url: data.og_image_url,
|
||||
};
|
||||
|
||||
await dispatch(update({ id: id as string, data: apiData }));
|
||||
await router.push('/projects/projects-list');
|
||||
try {
|
||||
await dispatch(update({ id: id as string, data: apiData })).unwrap();
|
||||
toast('Project settings saved', {
|
||||
type: 'success',
|
||||
position: 'bottom-center',
|
||||
});
|
||||
} catch (error: unknown) {
|
||||
const errorMessage =
|
||||
error && typeof error === 'object' && 'message' in error
|
||||
? String((error as { message: string }).message)
|
||||
: 'Failed to save project settings';
|
||||
toast(errorMessage, {
|
||||
type: 'error',
|
||||
position: 'bottom-center',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = async () => {
|
||||
@ -322,6 +337,7 @@ const EditProjectsPage = () => {
|
||||
)}
|
||||
</Formik>
|
||||
</CardBox>
|
||||
<ToastContainer />
|
||||
</SectionMain>
|
||||
</>
|
||||
);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user