fix: MedSchedule ESM import for Router and data table links resolution

This commit is contained in:
2026-07-15 09:58:07 +03:00
parent f519ce95db
commit f560facabd
8 changed files with 791 additions and 76 deletions

View File

@@ -1,3 +1,4 @@
import { Router } from "express";
import type { Express, Request, Response, NextFunction } from "express";
import type { Response as ExpressResponse } from "express";
import { authenticateToken, type AuthenticatedRequest } from "../middleware/auth.middleware";
@@ -52,7 +53,7 @@ function parseDate(value: unknown): Date | null {
}
export function registerMedScheduleRoutes(app: Express) {
const router = require("express").Router();
const router = Router();
router.use(authenticateToken);
router.use(tenantIsolation);
router.use(medOrgResolver());

View File

@@ -707,6 +707,151 @@ export function registerDataTableRoutes(app: import("express").Express): void {
}
);
// =====================================================
// DATA TABLE LINKS API (подсправочники)
// =====================================================
// Get subdirectories for a parent table
app.get(['/api/tables/:id/subdirectories', '/api/directories/:id/subdirectories'],
authenticateToken,
tenantIsolation,
async (req: AuthenticatedRequest, res) => {
try {
const tableId = parseInt(req.params.id);
const table = await storage.getDataTable(tableId, req.organizationId!);
if (!table) {
return res.status(404).json({ error: 'Таблица не найдена' });
}
const role = await storage.getUserTableRole(tableId, req.user!.id, table.createdBy, req.organizationId!);
if (!role) {
return res.status(403).json({ error: 'Нет доступа к таблице' });
}
const links = await storage.getDataTableLinks(tableId, req.organizationId!);
res.json({ subdirectories: links });
} catch (error) {
console.error('Get subdirectories error:', error);
res.status(500).json({ error: 'Ошибка получения подсправочников' });
}
}
);
// Create a subdirectory link
app.post(['/api/tables/:id/subdirectories', '/api/directories/:id/subdirectories'],
authenticateToken,
tenantIsolation,
async (req: AuthenticatedRequest, res) => {
try {
const tableId = parseInt(req.params.id);
const table = await storage.getDataTable(tableId, req.organizationId!);
if (!table) {
return res.status(404).json({ error: 'Таблица не найдена' });
}
const role = await storage.getUserTableRole(tableId, req.user!.id, table.createdBy, req.organizationId!);
if (role !== 'admin') {
return res.status(403).json({ error: 'Нет прав для управления таблицей' });
}
const { childTableId, parentColumnIndex, childColumnIndex, parentValues } = req.body;
if (!childTableId || typeof childTableId !== 'number') {
return res.status(400).json({ error: 'Необходимо указать подсправочник (childTableId)' });
}
const childTable = await storage.getDataTable(childTableId, req.organizationId!);
if (!childTable) {
return res.status(404).json({ error: 'Подсправочник не найден' });
}
const link = await storage.createDataTableLink({
organizationId: req.organizationId!,
parentTableId: tableId,
childTableId,
parentColumnIndex: parentColumnIndex ?? 0,
childColumnIndex: childColumnIndex ?? 0,
parentValues: Array.isArray(parentValues) ? parentValues : [],
});
res.status(201).json({ link });
} catch (error: any) {
console.error('Create subdirectory error:', error);
res.status(400).json({ error: error.message || 'Ошибка создания подсправочника' });
}
}
);
// Update a subdirectory link
app.patch(['/api/tables/:id/subdirectories/:linkId', '/api/directories/:id/subdirectories/:linkId'],
authenticateToken,
tenantIsolation,
async (req: AuthenticatedRequest, res) => {
try {
const tableId = parseInt(req.params.id);
const linkId = parseInt(req.params.linkId);
const table = await storage.getDataTable(tableId, req.organizationId!);
if (!table) {
return res.status(404).json({ error: 'Таблица не найдена' });
}
const role = await storage.getUserTableRole(tableId, req.user!.id, table.createdBy, req.organizationId!);
if (role !== 'admin') {
return res.status(403).json({ error: 'Нет прав для управления таблицей' });
}
const existing = await storage.getDataTableLink(linkId, req.organizationId!);
if (!existing || existing.parentTableId !== tableId) {
return res.status(404).json({ error: 'Связь не найдена' });
}
const { childTableId, parentColumnIndex, childColumnIndex, parentValues } = req.body;
const updates: Partial<import("@shared/schema").DataTableLink> = {};
if (childTableId !== undefined) updates.childTableId = childTableId;
if (parentColumnIndex !== undefined) updates.parentColumnIndex = parentColumnIndex;
if (childColumnIndex !== undefined) updates.childColumnIndex = childColumnIndex;
if (parentValues !== undefined) updates.parentValues = Array.isArray(parentValues) ? parentValues : [];
const link = await storage.updateDataTableLink(linkId, req.organizationId!, updates);
res.json({ link });
} catch (error: any) {
console.error('Update subdirectory error:', error);
res.status(400).json({ error: error.message || 'Ошибка обновления подсправочника' });
}
}
);
// Delete a subdirectory link
app.delete(['/api/tables/:id/subdirectories/:linkId', '/api/directories/:id/subdirectories/:linkId'],
authenticateToken,
tenantIsolation,
async (req: AuthenticatedRequest, res) => {
try {
const tableId = parseInt(req.params.id);
const linkId = parseInt(req.params.linkId);
const table = await storage.getDataTable(tableId, req.organizationId!);
if (!table) {
return res.status(404).json({ error: 'Таблица не найдена' });
}
const role = await storage.getUserTableRole(tableId, req.user!.id, table.createdBy, req.organizationId!);
if (role !== 'admin') {
return res.status(403).json({ error: 'Нет прав для управления таблицей' });
}
const existing = await storage.getDataTableLink(linkId, req.organizationId!);
if (!existing || existing.parentTableId !== tableId) {
return res.status(404).json({ error: 'Связь не найдена' });
}
await storage.deleteDataTableLink(linkId, req.organizationId!);
res.json({ success: true });
} catch (error: any) {
console.error('Delete subdirectory error:', error);
res.status(400).json({ error: error.message || 'Ошибка удаления подсправочника' });
}
}
);
// Log data table Excel/CSV export
app.post(['/api/tables/:tableId/export', '/api/directories/:tableId/export'],
authenticateToken,

View File

@@ -1,4 +1,4 @@
import { users, dataTables, dataTableRows, dataTablePermissions, dataTableAccessRules, formFields, forms, formAccessRules, roleMembers, type DataTable, type DataTableRow, type DataTablePermission, type InsertDataTable, type InsertDataTableRow, type InsertDataTablePermission, type DataTableFull, type DataTableAccessRule, type InsertDataTableAccessRule } from "@shared/schema";
import { users, dataTables, dataTableRows, dataTablePermissions, dataTableAccessRules, dataTableLinks, formFields, forms, formAccessRules, roleMembers, type DataTable, type DataTableRow, type DataTablePermission, type InsertDataTable, type InsertDataTableRow, type InsertDataTablePermission, type DataTableFull, type DataTableAccessRule, type InsertDataTableAccessRule, type DataTableLink, type DataTableSubdirectory } from "@shared/schema";
import { db } from "../db";
import { eq, and, desc, sql, inArray } from "drizzle-orm";
import { BotsStorage } from "./bots.storage";
@@ -56,17 +56,53 @@ export class DataTablesCoreStorage extends BotsStorage {
return tablesWithAccess;
}
// =====================================================
// DATA TABLE LINKS (подсправочники)
// =====================================================
async getDataTableLinks(parentTableId: number, organizationId: number): Promise<DataTableSubdirectory[]> {
const links = await db
.select({
id: dataTableLinks.id,
organizationId: dataTableLinks.organizationId,
parentTableId: dataTableLinks.parentTableId,
childTableId: dataTableLinks.childTableId,
parentColumnIndex: dataTableLinks.parentColumnIndex,
childColumnIndex: dataTableLinks.childColumnIndex,
parentValues: dataTableLinks.parentValues,
createdAt: dataTableLinks.createdAt,
updatedAt: dataTableLinks.updatedAt,
childTable: {
id: dataTables.id,
name: dataTables.name,
columns: dataTables.columns,
},
})
.from(dataTableLinks)
.innerJoin(dataTables, eq(dataTableLinks.childTableId, dataTables.id))
.where(and(
eq(dataTableLinks.parentTableId, parentTableId),
eq(dataTableLinks.organizationId, organizationId),
eq(dataTables.isDeleted, false)
))
.orderBy(dataTableLinks.id);
return links as DataTableSubdirectory[];
}
async getDataTableFull(id: number, organizationId: number): Promise<DataTableFull | undefined> {
const table = await this.getDataTable(id, organizationId);
if (!table) return undefined;
const rows = await this.getDataTableRows(id, organizationId);
const permissions = await this.getDataTablePermissions(id, organizationId);
const subdirectories = await this.getDataTableLinks(id, organizationId);
return {
...table,
rows,
permissions,
subdirectories,
};
}
@@ -312,6 +348,66 @@ export class DataTablesCoreStorage extends BotsStorage {
.where(eq(dataTableRows.tableId, tableId));
}
// =====================================================
// DATA TABLE LINKS (подсправочники)
// =====================================================
async getDataTableLink(id: number, organizationId: number): Promise<DataTableLink | undefined> {
const [link] = await db
.select()
.from(dataTableLinks)
.where(and(
eq(dataTableLinks.id, id),
eq(dataTableLinks.organizationId, organizationId)
));
return link || undefined;
}
async createDataTableLink(data: { organizationId: number; parentTableId: number; childTableId: number; parentColumnIndex?: number; childColumnIndex?: number; parentValues?: string[] }): Promise<DataTableLink> {
const [link] = await db
.insert(dataTableLinks)
.values({
organizationId: data.organizationId,
parentTableId: data.parentTableId,
childTableId: data.childTableId,
parentColumnIndex: data.parentColumnIndex ?? 0,
childColumnIndex: data.childColumnIndex ?? 0,
parentValues: data.parentValues ?? [],
})
.onConflictDoUpdate({
target: [dataTableLinks.parentTableId, dataTableLinks.childTableId],
set: {
parentColumnIndex: data.parentColumnIndex ?? 0,
childColumnIndex: data.childColumnIndex ?? 0,
parentValues: data.parentValues ?? [],
updatedAt: new Date(),
},
})
.returning();
return link;
}
async updateDataTableLink(id: number, organizationId: number, updates: Partial<DataTableLink>): Promise<DataTableLink> {
const [link] = await db
.update(dataTableLinks)
.set({ ...updates, updatedAt: new Date() })
.where(and(
eq(dataTableLinks.id, id),
eq(dataTableLinks.organizationId, organizationId)
))
.returning();
return link;
}
async deleteDataTableLink(id: number, organizationId: number): Promise<void> {
await db
.delete(dataTableLinks)
.where(and(
eq(dataTableLinks.id, id),
eq(dataTableLinks.organizationId, organizationId)
));
}
async applyDataTableSync(
tableId: number,
deleted: { id: number }[],