14 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
This is a Chinese Tobacco AI Contract and Case Audit System (中国烟草AI合同及卷宗审核系统) - a Remix-based full-stack application for intelligent document review and evaluation. The system provides AI-powered contract auditing, risk assessment, and compliance checking.
Tech Stack:
- Frontend: Remix (React) + TypeScript + Vite
- Styling: Tailwind CSS + custom CSS
- Icons: Remixicon (locally hosted)
- Document Processing: react-pdf, mammoth, docx-preview
- Backend API: PostgreSQL + PostgREST
- Authentication: OAuth2.0 + JWT + cookie-based sessions
- Deployment: Docker + PM2 (multi-instance setup)
Core Commands
Development
npm run dev # Start dev server (port 5173)
npm run typecheck # Run TypeScript type checking
npm run lint # Run ESLint
Building
npm run build # Production build
npm run build:production:multi # Build for production multi-instance
npm run build:test:multi # Build for testing multi-instance
npm run build:dev # Build for development
Production Deployment
npm start # Start production server (single instance)
npm run start:pm2:production:multi # Build and start PM2 multi-instance (production)
npm run start:pm2:multi # Build and start PM2 multi-instance (testing)
JWT Secret Generation
npm run generate:jwt-secret # Generate a secure JWT secret for production
Docker Deployment
# Build image
docker build -t docreview-app .
# Run with docker-compose
docker-compose up -d
# The system exposes 6 ports for different regional clients:
# 51703: Meizhou (main)
# 51704: Yunfu
# 51705: Jieyang
# 51706: Chaozhou
# 51707: Province
# 51708: Test instance (limited to /cross-checking routes)
Architecture
Multi-Instance Deployment Strategy
The system uses a port-based multi-client architecture where:
- One codebase serves multiple regional clients
- Each client runs on a different port with isolated configurations
- Port-specific API configurations are defined in
app/config/api-config.ts - PM2 manages multiple instances via
ecosystem.config.cjs - Each instance has its own environment variables (PORT, CLIENT_ID, API_PORT_CONFIG)
Authentication Flow
-
OAuth2.0 Integration (IDaaS-based):
- OAuth client configuration in
app/api/login/oauth-client.ts - Callback handling in
app/routes/callback.tsx - User info sync with local PostgreSQL database
- OAuth client configuration in
-
JWT Token Management:
- Frontend JWT generated after OAuth success (
app/api/jwt-helper.server.ts) - JWT contains user info, role, and permissions
- Stored in encrypted cookie session
- Auto-refresh when OAuth token refreshes
- CRITICAL: JWT_SECRET must be set in
.env(usenpm run generate:jwt-secret)
- Frontend JWT generated after OAuth success (
-
Session Management:
- Cookie-based sessions via
createCookieSessionStorage - Global authentication check in
app/root.tsxloader - Role-based access control (common vs developer roles)
- Developer-only paths: /settings, /config-lists, /document-types, /prompts
- Cookie-based sessions via
API Configuration System
Port-Based Configuration (app/config/api-config.ts):
- Automatically detects current port and applies correct API config
- Each port maps to specific backend services (PostgreSQL, MinIO, etc.)
- Environment variables can override port configs:
NEXT_PUBLIC_API_BASE_URL- PostgreSQL API base URLNEXT_PUBLIC_DOCUMENT_URL- MinIO document storage URLNEXT_PUBLIC_UPLOAD_URL- File upload endpoint
Configuration Priority: Port-specific config > Environment variables > Default environment config
Route Structure
app/routes/- Remix file-based routing_index.tsx- Home page (redirects to /documents or /cross-checking based on port)login.tsx- Login page with OAuth flowcallback.tsx- OAuth callback handlerdocuments.tsx/documents._index.tsx- Document managementcross-checking.tsx- Cross-examination systemcontract-template.tsx- Contract template searchchat-with-llm._index.tsx- AI chat interfaceapi.*.tsx- API routes for backend proxy
API Layer Organization
app/api/- API client modulesaxios-client.ts- Configured axios instance with auth headerspostgrest-client.ts- PostgREST API clientdb-client.server.ts- Server-side database clientjwt-helper.server.ts- JWT generation and validationlogin/auth.server.ts- Authentication service (getUserSession, logout)login/oauth-client.ts- OAuth2.0 client implementationlogin/token-manager.server.ts- Token refresh and management
Important: Always use axios-client.ts for API calls. It automatically:
- Adds JWT authentication headers
- Handles API base URL configuration
- Includes proper error handling
Component Architecture
Design System (app/components/ui/):
- All components follow BEM naming convention
- Primary color:
#00684a(tobacco corporate green) - Components are self-contained with co-located styles
- Import styles in
app/styles/main.css
Key Components:
Layout.tsx- Main layout with sidebar and headerSidebar.tsx- Navigation sidebar (role-based menu filtering)MessageModal.tsx- Confirmation/alert modal systemToast.tsx- Toast notification providerLoadingBar.tsx- Top loading bar for route transitionsFilePreview.tsx- PDF/Word document preview (react-pdf + Collabora integration)
Document Preview System
Current Implementation (app/components/reviews/FilePreview.tsx):
- PDF files: Rendered using
react-pdflibrary - DOCX files: Needs Collabora Online integration (planned)
Integration Plan - Collabora Online for DOCX Preview:
The FilePreview component should support multiple file types:
.pdf→ Use react-pdf (current implementation).docx→ Use Collabora Online viewer (to be integrated from collabora-test project)
Key Pages Using FilePreview:
app/routes/reviews.tsx- Document review page (usesdocument.path)app/routes/contract-template.detail.$id.tsx- Contract template details (usestemplate.file_path)
Data Flow for Collabora Integration:
Page Component (reviews.tsx / contract-template.detail.$id.tsx)
↓ passes fileContent.path
FilePreview Component (detects file extension)
↓ if .docx
CollaboraViewer Component (app/components/collabora/)
↓ calls API
app/routes/api.collabora.config.tsx (Remix loader)
↓ generates JWT + WOPISrc URL
↓ returns { iframeUrl, accessToken }
CollaboraViewer renders iframe
↓ Collabora Online loads document
↓ calls WOPI endpoints
app/routes/api.collabora.wopi.files.$fileId.tsx (Remix loader/action)
↓ CheckFileInfo (GET) / GetFile (GET /contents) / PutFile (POST /contents)
↓ interacts with MinIO storage
Returns document data to Collabora
Files to Migrate from collabora-test:
CollaboraViewer.tsx- Main viewer componenthooks.ts- useCollaboraConfig, useCollaboraUICustomization, useDocumentReady, useCollaboraUnoCommandsapi.ts- API client for Collabora configUno.ts- LibreOffice UNO commands wrapperCollaboraIframeUI.ts- UI customization utilitiestypes.ts- TypeScript type definitions
Backend API Routes to Create:
app/routes/api.collabora.config.tsx- Generate Collabora iframe URL and JWT tokenapp/routes/api.collabora.wopi.files.$fileId.tsx- WOPI protocol implementation (CheckFileInfo, GetFile, PutFile)
Environment Variables Needed:
# Collabora Online server URL
COLLABORA_URL=http://10.79.97.17:9980
# Application base URL (must be accessible from Collabora server)
APP_URL=http://10.79.97.17:51703
# JWT secret for WOPI token signing (reuse existing JWT_SECRET)
Security Considerations:
- JWT token must include
fileIdfor WOPI endpoint validation - File path sanitization to prevent directory traversal attacks
- CORS configuration for Collabora server to access WOPI endpoints
- WOPI CheckFileInfo should return pure JSON (not wrapped in API response format)
File Type Detection in FilePreview:
const fileExtension = fileContent.path.split('.').pop()?.toLowerCase();
if (fileExtension === 'pdf') {
// Use react-pdf
return <PDFViewer />;
} else if (fileExtension === 'docx') {
// Use Collabora
return <CollaboraViewer fileId={fileContent.path} mode="view" />;
} else {
// Unsupported format
return <UnsupportedFileMessage />;
}
Reference Implementation:
- See
collabora-testworkspace for complete working example - Adapt Next.js API routes to Remix loader/action pattern
- Convert Next.js
route.tsGET/POST to Remixloader()andaction()functions
RemixIcon Usage:
- Icons are locally hosted in
public/fonts/ - Use class syntax:
<i className="ri-home-line"></i> - Preloaded via link tag for instant display
- CRITICAL: When using CSS isolation (like
all: unset), add exception rules for RemixIcon classes (seedocs/docreview-development-standards.mdfor examples)
Cross-Checking System
Overview: Democratic proposal and voting system for document evaluation disputes.
Key Concepts:
- Tasks: Evaluation assignments with multiple reviewers
- Proposals: Suggestions to modify evaluation scores
- Voting: Democratic decision-making with approval threshold
- Arbitration: Automatic status determination based on vote counts
Database Tables:
cross_examination_tasks- Task assignmentscross_task_document_mapping- Document-task relationshipscross_scoring_proposals- Score modification proposalscross_opinion_votes- Vote records
API Routes (via app/routes/api.*.tsx proxies):
POST /admin/cross_review/tasks/assign- Assign cross-checking taskPOST /admin/cross_review/proposals- Create proposalPOST /admin/cross_review/proposals/{id}/votes- Vote on proposalDELETE /admin/cross_review/proposals/{id}- Withdraw proposal
See docs/交叉评查系统完整文档.md for complete documentation.
Environment Variables
Required (create .env file):
# JWT Secret - MUST be set for production
JWT_SECRET=your-super-secret-jwt-key-change-this-in-production
# OAuth2.0 Configuration (set in PM2 config or env)
OAUTH_CLIENT_SECRET=your-oauth-client-secret
# API Configuration (optional, overrides port-based config)
NEXT_PUBLIC_API_BASE_URL=http://10.79.97.17:8000
NEXT_PUBLIC_DOCUMENT_URL=http://10.76.244.156:9000/docauditai/
NEXT_PUBLIC_UPLOAD_URL=http://10.79.97.17:8000/admin/documents
PM2 Instance Variables (set in ecosystem.config.cjs):
NODE_ENV- Environment (production/testing/development)PORT- Server portCLIENT_ID- Regional client identifierAPI_PORT_CONFIG- Port-based API configuration selector
Development Guidelines
Code Style
TypeScript:
- Use explicit types for function parameters and return values
- Interface naming: PascalCase (e.g.,
DocumentUI,ApiConfig) - Use string literal unions for status types
- Avoid
any- useunknownif type is truly unknown
React Components:
- Use function declarations, not arrow functions:
export function Component() {} - Group hooks at top: useState first, then useEffect
- Event handlers:
handleClick,handleSubmitnaming - Props interface:
ComponentNameProps
File Naming:
- Components:
PascalCase.tsx(Button.tsx) - Routes:
kebab-case.tsx(user-profile.tsx) - Utilities:
camelCase.ts(utils.ts)
Import Order:
- React and Remix imports
- Third-party libraries
- Internal components with
~/alias - Type imports
- Styles
Styling
Color Variables (defined in app/root.tsx):
--color-primary: #00684a; /* Tobacco green */
--color-primary-hover: #005a3f;
--color-primary-light: rgba(0, 104, 74, 0.1);
--color-success: #52c41a;
--color-warning: #faad14;
--color-error: #f5222d;
Spacing: Use Tailwind defaults (4px base: p-4, mt-6, etc.)
Border Radius: rounded-md (6px) for buttons/cards
Shadows: shadow-sm default, shadow-md on hover
Error Handling
- Use
ErrorBoundarycomponent for route-level errors - API errors: Log and return appropriate Response.json with status
- Display user-friendly messages via MessageModal or Toast
- Never expose sensitive error details to users
Performance
- Use
React.memofor pure components that re-render frequently - Lazy load heavy components (PDF viewers, code editors)
- Debounce search inputs (300ms standard)
- Optimize images and use appropriate formats
Security Considerations
- JWT Secret: NEVER commit JWT_SECRET to version control
- OAuth Client Secret: Store in environment variables, not code
- API Keys: Use server-side only (no NEXT_PUBLIC_ prefix)
- Session Cookies: httpOnly, secure in production
- Port 51708 Restrictions: Limited to /cross-checking routes only
Testing
- Test OAuth flow with IDaaS server at
http://10.79.112.85 - Verify JWT generation and validation
- Check port-based API configuration switching
- Test role-based access control (common vs developer)
- Validate cross-checking voting logic and thresholds
Common Pitfalls
- RemixIcon not showing: Check for CSS rules overriding
font-family: 'remixicon' - API config not updating: Port detection may need window.location.port on client
- JWT expired: Implement token refresh or re-authenticate
- PM2 env vars not working: Ensure NEXT_PUBLIC_ prefix for client-side vars
- File upload fails: Check UPLOAD_URL configuration for current port
Additional Documentation
Detailed documentation is available in docs/:
docreview-development-standards.md- Comprehensive coding standards交叉评查系统完整文档.md- Cross-checking system specificationJWT_IMPLEMENTATION.md- JWT authentication implementationOAuth2.0认证协议集成指南.md- OAuth integration guidedocker-deployment.md- Docker deployment instructionsdeployment-config.md- Multi-instance deployment configuration