- number-поля теперь рендерятся как text + inputMode=numeric, чтобы браузер не округлял значения через input type=number - пробелы при вставке в number-поля удаляются - бэкенд нормализует значения number-полей в строку перед сохранением - добавлен хелпер normalizeFieldValueForStorage Closes: искажение расчётного счёта и других длинных числовых полей
12 KiB
Overview
This is a multitenant corporate iistwin platform with a React frontend and an Express backend, designed to provide organizations with independent management of users, projects, and workflows on shared infrastructure. It ensures data isolation, scalability, and security, offering real-time communication and efficient task handling. Key capabilities include custom JS pages, data tables, advanced workflow transitions with conditional approvals, hierarchical subtasks, inline editable tables, and an event-driven notification system.
User Preferences
Preferred communication style: Simple, everyday language. Preferred language: Russian — always respond in Russian, no exceptions.
System Architecture
Frontend
The frontend uses React with TypeScript and Vite, employing a component-based architecture with shadcn/ui, Radix UI, and Tailwind CSS. Wouter handles routing, TanStack Query manages server state, and React Hook Form with Zod is used for form handling. Authentication is context-based using JWT tokens.
Backend
The backend is built with Express.js, featuring a layered architecture (API, Service, Data Access) and Drizzle ORM for database abstraction. Custom middleware enforces tenant isolation, authentication, and request validation.
Authentication & Authorization
JWT-based authentication uses access and refresh tokens. Role-based access (Admin, User) is enforced, and bcrypt secures passwords.
Super Admin System
A separate system-level super admin panel exists at /superadmin (login at /superadmin/login). Super admins are stored in the super_admins table (no organizationId — system-level users). They authenticate via a separate JWT with { isSuperAdmin: true, audience: 'workflow-superadmin' } checked by requireSuperAdmin middleware. The panel shows all organizations with stats (users, tasks, last activity) and allows toggling organization access. First super admin is created via POST /api/superadmin/seed (protected by optional SUPERADMIN_SEED_TOKEN env var).
Multitenancy
Tenant isolation is implemented at the database (via organizationId), API (middleware filtering), and authentication levels.
Database
A PostgreSQL database with Drizzle ORM is used, featuring core entities like Organizations, Users, and User Sessions with serial primary keys and foreign key relationships.
Security & Legal Compliance
HTTP Security Headers
helmet middleware is configured in server/index.ts with a Content Security Policy that allows unsafe-eval and unsafe-inline for the Monaco editor and Babel runtime. crossOriginEmbedderPolicy is disabled to support iframe embeds. frameguard (X-Frame-Options) is disabled and frame-ancestors allows https://*.replit.dev, https://*.repl.co, https://*.replit.com so the app can be embedded in the Replit IDE preview and Canvas.
HSTS and upgrade-insecure-requests are conditionally enabled per-request when the server detects it is behind an HTTPS reverse proxy. Detection uses two conditions (either is sufficient):
HTTPS_PROXY=trueenv var — static override, set in docker-compose or deployment config.X-Forwarded-Proto: httpsrequest header — sent automatically by Nginx/Traefik/Caddy when proxying HTTPS traffic.
When either condition is true: Strict-Transport-Security: max-age=31536000; includeSubDomains; preload and CSP upgrade-insecure-requests are added to the response.
When neither is true (plain HTTP dev/local): these headers are omitted.
Personal Data (152-ФЗ Compliance)
- Consent checkbox with link to
/privacy-policyis required on both registration forms (Register.tsx and InviteRegister.tsx) - Privacy Policy page at
/privacy-policy(PrivacyPolicy.tsx) explains data processing in accordance with Russian Federal Law 152-ФЗ - Known remaining gaps (for production deployments): DB localization to Russian hosting, replace SendGrid with a Russian email provider, add user data export/deletion self-service UI
Remaining xlsx Vulnerabilities
The xlsx (SheetJS) package has 1 high + 7 moderate known vulnerabilities with no fix available upstream. This affects the Excel import/export feature for data tables.
Real-time Communication
The system includes real-time chat with optimistic updates and @mentions. Server-Sent Events (SSE) provide instant read receipts and task_updated events, with polling as a fallback.
Server-Side TTL Cache
A lightweight in-memory TtlCache (server/utils/cache.ts) is used for frequently accessed data like forms and minimal task lists, with a 60-second TTL and invalidation on data changes.
Custom Data Tables
Organizations can create custom data tables with named columns, inline editing, three-tier access control, Excel import/export, and integration as a form field type.
Workflow Transitions
Forms support customizable statuses and transitions with conditional approvals, dynamic approver assignment, and real-time UI updates via SSE.
Task Tabs System
Task fields can be organized into configurable tabs within the form editor, supporting 'fields' or 'table' types. A modular plugin system allows for new tab types and custom declarative tab modules (JSON configurations). These modules support various component types, including info_panel, image_gallery, chart, tabs, layout, and formula, allowing user input storage in task_tab_values.
JS Code Editor for Custom Tabs
A js_component module type enables writing arbitrary React+JSX code in the browser using the Monaco Editor. Code is executed in a sandboxed environment via @babel/standalone and new Function(), with a ctx object providing access to React, UI components, API calls, user info, task data, and messaging functions.
Regular Tables
Forms can include 'table' type tabs, providing customizable tables with inline editing, various column types (text, number, date, select, table reference), resizable columns, and compact display.
Hierarchical Subtasks
Tasks support unlimited nesting of subtasks with parent-child relationships, automatic numbering, depth tracking, and UI navigation.
Event-Driven Notification System
An event-driven notification system allows users to subscribe to events like task.created or task.status.changed, delivering notifications via in-app alerts, email, and real-time push via SSE. @mentions always trigger notifications.
Billing System
A multi-tenant billing system allows super admins to manage organization balances and pricing. Key features:
organization_billingtable: per-org settings (balance, pricePerUser, currency, nextBillingDate, blockedAt)billing_transactionstable: credit/debit transaction history with descriptionsbillingBlockedflag on organizations for fast access checks- Billing cycle worker: runs every 24h, charges
activeUsers × pricePerUser, blocks orgs with insufficient balance - Super admin panel: view/edit billing settings, top up balance, unblock orgs, view transaction history
- Org admin billing page at
/billing: read-only view of balance, active users, estimated charge, transaction history billingBlockedcheck inauthenticateTokenmiddleware returns HTTP 402 for non-whitelisted endpoints- Whitelist:
/api/auth/,/api/superadmin/,/api/billing/,/api/health - Layout.tsx shows a blocking overlay when
billingBlocked=true(except on/billingroute itself) - Billing link added to the sidebar dropdown for admins
File Upload System
Form fields of type file allow users to upload files to tasks. The system supports two storage backends selected automatically via environment variables:
Local disk mode (default/Replit/dev): Files saved to uploads/, served via express.static at /uploads/. No extra config needed.
S3/MinIO mode (production/Docker): Activated when MINIO_ENDPOINT env var is set. Files uploaded to MinIO S3 bucket via @aws-sdk/client-s3. Served via proxy endpoint GET /api/files/:key (streams from S3). File deletion uses DeleteObjectCommand. Helper: server/utils/s3.ts.
Both modes: upload endpoint POST /api/upload (multer, 10 MB limit, MIME-type allowlist), protected by authenticateToken. Stored field values are JSONB objects { url, name, size }. File deletion correctly handles both single files and arrays — only removes files missing from the new value (deleteRemovedFiles).
Docker deployment: docker-compose.yml has with-minio profile adding MinIO (ports 9000/9001) and WebDAV via rclone (port 8080) for file browsing from Windows Explorer/Finder. See DOCKER.md for full instructions.
Task Audit Log
A chronological history of all task changes is stored in task_audit_log, tracking actions like creation, status changes, updates, and field value modifications. This log is accessible via an API and displayed in the UI as a timeline.
Inline Editing of Task Form Fields
Custom form field values can be edited directly on the task detail page. This feature provides type-specific editors, save/cancel actions, and records changes in the task_audit_log.
Task Reminders System
Tasks support scheduled reminders with flexible recipient configuration (individual users or roles). A background worker processes and sends these reminders via the notification system.
Push Notifications Infrastructure
The system includes infrastructure for mobile push notifications with tenant isolation, using device_tokens for FCM/APNs, user_presence for online status, and a push queue for offline users.
VAPID Keys for Web Push
Web push notifications require stable VAPID keys. Without them, new keys are generated on every server restart, invalidating all existing browser push subscriptions.
Setup (one-time): Generate permanent keys and set them as environment secrets:
npx web-push generate-vapid-keys
Set the output as VAPID_PUBLIC_KEY and VAPID_PRIVATE_KEY in your environment secrets (Replit Secrets tab or .env for Docker). If keys are missing, the server logs a warning and generates temporary keys for that session only.
See DOCKER.md → "Генерация VAPID-ключей для веб-пушей" for Docker deployment instructions.
Mobile UI Adaptations
The UI adapts for mobile devices (screen width <768px), with chat always in a separate tab and a visible tab selector.
Progressive Web App (PWA)
The system supports PWA installation, offering offline access and push notifications via manifest.json, a Service Worker (sw.js), and a PWA Install Button.
Global Fields System
Reusable global fields with pre-filled settings (type, options, validation) can be used across all organization forms. They serve as templates, and changes to global fields do not affect existing form fields.
Automations System
JavaScript automations stored in the automations table allow server-side code execution in a Node.js vm sandbox in response to triggers (manual, task.created, task.status_changed). A ctx object provides logging and access to forms and tasks.
JS Layout Editor for Task Detail Page
Admins can write custom React/JSX components to replace the default field display on any task detail page. The code is stored in forms.detailLayoutCode and edited via a Monaco editor in the FormEditor.
Task Relations System
Many-to-many task relations are stored in task_relations, created automatically via "task" type fields or manually via a "Link" button in chat. Related tasks are displayed in a dedicated UI tab, showing a flattened list grouped by form.
Performance Optimizations
Optimizations include aggregated API endpoints, React Query caching, server-side pagination with sorting, strategic database indexing, and server-side computation of user fullName.
External Dependencies
Database
- Neon Database: Serverless PostgreSQL database.
- Drizzle ORM: Type-safe database toolkit.
Authentication & Security
- JWT: JSON Web Tokens.
- bcrypt: Password hashing.
- Express rate limiter: For API protection.
Email Services
- SendGrid: Email delivery service.
UI Frameworks
- Radix UI: Unstyled, accessible UI primitives.
- Tailwind CSS: Utility-first CSS framework.
- shadcn/ui: Pre-built component library.
Development Tools
- Vite: Build tool and development server.
- TypeScript: Static type checking.
Code Editor & JS Runtime
- @monaco-editor/react: Monaco Editor for in-browser JavaScript editing.
- @babel/standalone: In-browser Babel for JSX transformation.
HTML Sanitization
- dompurify: For safe HTML rendering.