- number-поля теперь рендерятся как text + inputMode=numeric, чтобы браузер не округлял значения через input type=number - пробелы при вставке в number-поля удаляются - бэкенд нормализует значения number-полей в строку перед сохранением - добавлен хелпер normalizeFieldValueForStorage Closes: искажение расчётного счёта и других длинных числовых полей
187 lines
7.6 KiB
TypeScript
187 lines
7.6 KiB
TypeScript
import { Router } from "express";
|
|
import { z } from 'zod';
|
|
import { storage } from "../storage";
|
|
import { authenticateToken, type AuthenticatedRequest } from "../middleware/auth.middleware";
|
|
import { tenantIsolation } from "../middleware/tenant.middleware";
|
|
import { validateRequest } from "../middleware/validation.middleware";
|
|
import { insertBookmarkFolderSchema, createBookmarkFolderSchema } from "@shared/schema";
|
|
|
|
export function registerBookmarkFolderRoutes(router: Router): void {
|
|
// Get all bookmark folders for organization
|
|
router.get('/api/bookmark-folders',
|
|
authenticateToken,
|
|
tenantIsolation,
|
|
async (req: AuthenticatedRequest, res) => {
|
|
try {
|
|
const folders = await storage.getBookmarkFoldersByOrganization(req.organizationId!, req.user!.id);
|
|
res.json({ success: true, folders });
|
|
} catch (error) {
|
|
console.error('Get bookmark folders error:', error);
|
|
res.status(500).json({ success: false, error: 'Ошибка при загрузке папок' });
|
|
}
|
|
}
|
|
);
|
|
|
|
// Get bookmark folders by parent
|
|
router.get('/api/bookmark-folders/by-parent/:parentId',
|
|
authenticateToken,
|
|
tenantIsolation,
|
|
async (req: AuthenticatedRequest, res) => {
|
|
try {
|
|
const parentId = req.params.parentId === 'null' ? null : parseInt(req.params.parentId);
|
|
if (parentId !== null && isNaN(parentId)) {
|
|
return res.status(400).json({ success: false, error: 'Некорректный ID родительской папки' });
|
|
}
|
|
|
|
const folders = await storage.getBookmarkFoldersByParent(parentId, req.organizationId!, req.user!.id);
|
|
res.json({ success: true, folders });
|
|
} catch (error) {
|
|
console.error('Get bookmark folders by parent error:', error);
|
|
res.status(500).json({ success: false, error: 'Ошибка при загрузке дочерних папок' });
|
|
}
|
|
}
|
|
);
|
|
|
|
// Create bookmark folder
|
|
router.post('/api/bookmark-folders',
|
|
authenticateToken,
|
|
tenantIsolation,
|
|
validateRequest(createBookmarkFolderSchema),
|
|
async (req: AuthenticatedRequest, res) => {
|
|
try {
|
|
if (req.body.parentId) {
|
|
const parentFolder = await storage.getBookmarkFolder(req.body.parentId, req.organizationId!, req.user!.id);
|
|
if (!parentFolder) {
|
|
return res.status(400).json({ success: false, error: 'Родительская папка не найдена или не принадлежит вам' });
|
|
}
|
|
}
|
|
|
|
const rawFolderData = {
|
|
...req.body,
|
|
organizationId: req.organizationId!,
|
|
userId: req.user!.id,
|
|
createdBy: req.user!.id,
|
|
isExpanded: true
|
|
};
|
|
|
|
const parsedFolder = insertBookmarkFolderSchema.safeParse(rawFolderData);
|
|
if (!parsedFolder.success) {
|
|
return res.status(400).json({
|
|
success: false,
|
|
error: 'Некорректные данные папки',
|
|
details: parsedFolder.error.flatten()
|
|
});
|
|
}
|
|
|
|
const createdFolder = await storage.createBookmarkFolder(parsedFolder.data);
|
|
res.status(201).json({ success: true, message: 'Папка создана', folder: createdFolder });
|
|
} catch (error) {
|
|
console.error('Create bookmark folder error:', error);
|
|
res.status(500).json({ success: false, error: 'Ошибка при создании папки' });
|
|
}
|
|
}
|
|
);
|
|
|
|
// Update bookmark folder
|
|
router.put('/api/bookmark-folders/:id',
|
|
authenticateToken,
|
|
tenantIsolation,
|
|
async (req: AuthenticatedRequest, res) => {
|
|
try {
|
|
const folderId = parseInt(req.params.id);
|
|
if (isNaN(folderId)) {
|
|
return res.status(400).json({ success: false, error: 'Некорректный ID папки' });
|
|
}
|
|
|
|
const existingFolder = await storage.getBookmarkFolder(folderId, req.organizationId!, req.user!.id);
|
|
if (!existingFolder) {
|
|
return res.status(404).json({ success: false, error: 'Папка не найдена' });
|
|
}
|
|
|
|
const { userId: _uF, createdBy: _cbF, organizationId: _oF, id: _idF, ...folderUpdates } = req.body;
|
|
|
|
if (folderUpdates.parentId) {
|
|
const parentFolder = await storage.getBookmarkFolder(folderUpdates.parentId, req.organizationId!, req.user!.id);
|
|
if (!parentFolder) {
|
|
return res.status(400).json({ success: false, error: 'Родительская папка не найдена или не принадлежит вам' });
|
|
}
|
|
}
|
|
|
|
const updatedFolder = await storage.updateBookmarkFolder(
|
|
folderId, req.organizationId!, folderUpdates, req.user!.id
|
|
);
|
|
|
|
res.json({ success: true, message: 'Папка обновлена', folder: updatedFolder });
|
|
} catch (error) {
|
|
console.error('Update bookmark folder error:', error);
|
|
res.status(500).json({ success: false, error: 'Ошибка при обновлении папки' });
|
|
}
|
|
}
|
|
);
|
|
|
|
// Delete bookmark folder
|
|
router.delete('/api/bookmark-folders/:id',
|
|
authenticateToken,
|
|
tenantIsolation,
|
|
async (req: AuthenticatedRequest, res) => {
|
|
try {
|
|
const folderId = parseInt(req.params.id);
|
|
if (isNaN(folderId)) {
|
|
return res.status(400).json({ success: false, error: 'Некорректный ID папки' });
|
|
}
|
|
|
|
const existingFolder = await storage.getBookmarkFolder(folderId, req.organizationId!, req.user!.id);
|
|
if (!existingFolder) {
|
|
return res.status(404).json({ success: false, error: 'Папка не найдена' });
|
|
}
|
|
|
|
await storage.deleteBookmarkFolder(folderId, req.organizationId!, req.user!.id);
|
|
res.json({ success: true, message: 'Папка удалена' });
|
|
} catch (error) {
|
|
console.error('Delete bookmark folder error:', error);
|
|
res.status(500).json({ success: false, error: 'Ошибка при удалении папки' });
|
|
}
|
|
}
|
|
);
|
|
|
|
// Normalize bookmark folder positions
|
|
router.post('/api/bookmark-folders/normalize-positions',
|
|
authenticateToken,
|
|
tenantIsolation,
|
|
async (req: AuthenticatedRequest, res) => {
|
|
try {
|
|
await storage.normalizeBookmarkFolderPositions(req.organizationId!, req.user!.id);
|
|
res.json({ success: true, message: 'Позиции папок закладок нормализованы' });
|
|
} catch (error) {
|
|
console.error('Normalize bookmark folder positions error:', error);
|
|
res.status(500).json({ success: false, error: 'Ошибка при нормализации позиций папок закладок' });
|
|
}
|
|
}
|
|
);
|
|
|
|
// Reorder bookmark folders
|
|
router.post('/api/bookmark-folders/reorder',
|
|
authenticateToken,
|
|
tenantIsolation,
|
|
async (req: AuthenticatedRequest, res) => {
|
|
try {
|
|
const reorderItemSchema = z.object({
|
|
items: z.array(z.object({
|
|
id: z.number().int().positive(),
|
|
position: z.number().int().min(0),
|
|
})).min(1),
|
|
});
|
|
const parsed = reorderItemSchema.safeParse(req.body);
|
|
if (!parsed.success) {
|
|
return res.status(400).json({ success: false, error: 'Некорректный формат данных' });
|
|
}
|
|
await storage.batchUpdateBookmarkFolderPositions(parsed.data.items, req.organizationId!, req.user!.id);
|
|
res.json({ success: true });
|
|
} catch (error) {
|
|
console.error('Reorder bookmark folders error:', error);
|
|
res.status(500).json({ success: false, error: 'Ошибка при изменении порядка папок' });
|
|
}
|
|
}
|
|
);
|
|
}
|