generated from corrad-software/corrad-af-2024
818 lines
25 KiB
Markdown
818 lines
25 KiB
Markdown
# Electronic Document Management System (EDMS) - Technical Guide
|
|
|
|
## Table of Contents
|
|
1. [System Overview](#system-overview)
|
|
2. [Architecture](#architecture)
|
|
3. [Technology Stack](#technology-stack)
|
|
4. [Database Schema](#database-schema)
|
|
5. [Installation & Setup](#installation--setup)
|
|
6. [Development Environment](#development-environment)
|
|
7. [Component Structure](#component-structure)
|
|
8. [API & Data Management](#api--data-management)
|
|
9. [Security & Authentication](#security--authentication)
|
|
10. [Deployment](#deployment)
|
|
11. [Maintenance & Monitoring](#maintenance--monitoring)
|
|
12. [Extension & Customization](#extension--customization)
|
|
|
|
## System Overview
|
|
|
|
The Electronic Document Management System (EDMS) is a modern web application built with Nuxt.js 3 and Vue.js 3, designed to provide a comprehensive document management solution for organizations. The system implements a hierarchical document organization structure with role-based access control (RBAC) and supports integration with external authentication providers like Authentik.
|
|
|
|
### Key Technical Features
|
|
- **Modern Frontend**: Vue.js 3 with Composition API and script setup syntax
|
|
- **SSR/SPA Support**: Nuxt.js 3 for optimal performance and SEO
|
|
- **Database**: Prisma ORM with MySQL/PostgreSQL support and migrations
|
|
- **Styling**: TailwindCSS with custom component system
|
|
- **State Management**: Pinia for reactive state management with persistence
|
|
- **Authentication**: Flexible authentication with JWT tokens and RBAC
|
|
- **File Management**: Server-side file handling with metadata and versioning
|
|
- **Real-time Features**: Vue reactivity for live updates and notifications
|
|
- **Responsive Design**: Mobile-first approach with adaptive layouts
|
|
|
|
## Architecture
|
|
|
|
### System Architecture
|
|
```
|
|
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
|
│ Frontend │ │ Backend │ │ Database │
|
|
│ (Nuxt.js) │◄──►│ (Server API) │◄──►│ (MySQL/PG) │
|
|
│ │ │ │ │ │
|
|
│ • Vue.js 3 │ │ • Prisma ORM │ │ • User Data │
|
|
│ • TailwindCSS │ │ • File System │ │ • Documents │
|
|
│ • Pinia Store │ │ • Authentication│ │ • Permissions │
|
|
│ • Components │ │ • API Routes │ │ • Audit Logs │
|
|
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
|
│ │ │
|
|
│ ┌─────────────────┐ │
|
|
│ │ File Storage │ │
|
|
└──────────────►│ • Documents │◄────────────┘
|
|
│ • Versions │
|
|
│ • Thumbnails │
|
|
│ • Temp Files │
|
|
└─────────────────┘
|
|
```
|
|
|
|
### Application Flow
|
|
1. **Authentication**: User authenticates via configured authentication provider
|
|
2. **Authorization**: System validates user permissions against RBAC rules
|
|
3. **Navigation**: User navigates document hierarchy with tree-based structure
|
|
4. **Data Fetching**: Pinia store manages API calls with caching and error handling
|
|
5. **Rendering**: Vue components render UI with reactive updates
|
|
6. **File Operations**: Server handles secure file upload/download/preview
|
|
7. **Access Control**: System enforces granular document permissions
|
|
8. **Audit Trail**: All actions are logged for compliance and security
|
|
|
|
## Technology Stack
|
|
|
|
### Frontend Dependencies
|
|
```json
|
|
{
|
|
"core": {
|
|
"nuxt": "^3.6.5",
|
|
"vue": "^3.x",
|
|
"@pinia/nuxt": "^0.4.11",
|
|
"@pinia-plugin-persistedstate/nuxt": "^1.1.1"
|
|
},
|
|
"styling": {
|
|
"@nuxtjs/tailwindcss": "^6.8.0",
|
|
"sass": "^1.62.0",
|
|
"postcss-import": "^15.1.0"
|
|
},
|
|
"forms": {
|
|
"@formkit/nuxt": "^1.0.0",
|
|
"@formkit/themes": "^1.0.0",
|
|
"@formkit/addons": "^1.0.0",
|
|
"@formkit/pro": "^0.115.3"
|
|
},
|
|
"ui": {
|
|
"apexcharts": "^3.36.0",
|
|
"vue3-apexcharts": "^1.4.1",
|
|
"@vueuse/core": "^9.5.0",
|
|
"@vueuse/nuxt": "^9.5.0",
|
|
"floating-vue": "^2.0.0-beta.24",
|
|
"vue3-dropzone": "^2.0.1"
|
|
},
|
|
"utilities": {
|
|
"luxon": "^3.1.0",
|
|
"uuid": "^10.0.0",
|
|
"crypto-js": "^4.1.1",
|
|
"@shimyshack/uid": "^0.1.7"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Backend Dependencies
|
|
```json
|
|
{
|
|
"database": {
|
|
"@prisma/client": "^5.1.1",
|
|
"prisma": "^5.1.1"
|
|
},
|
|
"authentication": {
|
|
"jsonwebtoken": "^8.5.1"
|
|
},
|
|
"file-handling": {
|
|
"jspdf": "^2.5.1"
|
|
},
|
|
"security": {
|
|
"nuxt-security": "^0.13.0"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Development Tools
|
|
- **ESLint**: Code linting with Vue.js specific rules
|
|
- **TypeScript**: Type safety with Nuxt TypeScript integration
|
|
- **Prettier**: Consistent code formatting
|
|
- **Vite**: Lightning-fast build tool and HMR
|
|
- **PostCSS**: Advanced CSS processing with plugins
|
|
|
|
## Database Schema
|
|
|
|
### Core Models
|
|
|
|
#### User Management
|
|
```prisma
|
|
model user {
|
|
userID Int @id @default(autoincrement())
|
|
userSecretKey String? @db.VarChar(255)
|
|
userUsername String? @unique @db.VarChar(255)
|
|
userPassword String? @db.VarChar(255)
|
|
userFullName String? @db.VarChar(255)
|
|
userEmail String? @unique @db.VarChar(255)
|
|
userPhone String? @db.VarChar(255)
|
|
userStatus String? @db.VarChar(255)
|
|
userCreatedDate DateTime? @default(now()) @db.DateTime(0)
|
|
userModifiedDate DateTime? @updatedAt @db.DateTime(0)
|
|
|
|
// Relationships
|
|
userrole userrole[]
|
|
accessRequests AccessRequest[]
|
|
permissions AccessPermission[]
|
|
documents Document[] @relation("DocumentCreator")
|
|
cabinets Cabinet[] @relation("CabinetCreator")
|
|
drawers Drawer[] @relation("DrawerCreator")
|
|
folders Folder[] @relation("FolderCreator")
|
|
subfolders Subfolder[] @relation("SubfolderCreator")
|
|
}
|
|
|
|
model role {
|
|
roleID Int @id @default(autoincrement())
|
|
roleName String @unique @db.VarChar(255)
|
|
roleDescription String? @db.Text
|
|
rolePermissions Json? // Flexible permissions structure
|
|
roleCreatedDate DateTime @default(now()) @db.DateTime(0)
|
|
roleModifiedDate DateTime @updatedAt @db.DateTime(0)
|
|
|
|
userrole userrole[]
|
|
permissions AccessPermission[]
|
|
}
|
|
```
|
|
|
|
#### Document Hierarchy
|
|
```prisma
|
|
// Cabinet → Drawer → Folder → Subfolder → Document
|
|
|
|
model Cabinet {
|
|
id Int @id @default(autoincrement())
|
|
name String @db.VarChar(255)
|
|
description String? @db.Text
|
|
metadata Json? // Flexible metadata storage
|
|
createdAt DateTime @default(now()) @db.DateTime(0)
|
|
updatedAt DateTime @updatedAt @db.DateTime(0)
|
|
createdBy Int
|
|
status String @default("active") @db.VarChar(50)
|
|
|
|
user user @relation("CabinetCreator", fields: [createdBy], references: [userID])
|
|
drawers Drawer[]
|
|
permissions AccessPermission[]
|
|
|
|
@@index([createdBy])
|
|
@@index([status])
|
|
}
|
|
|
|
model Document {
|
|
id Int @id @default(autoincrement())
|
|
name String @db.VarChar(255)
|
|
description String? @db.Text
|
|
fileSize Int @default(0)
|
|
fileType String @db.VarChar(100)
|
|
fileExtension String @db.VarChar(20)
|
|
filePath String @db.VarChar(500)
|
|
thumbnailPath String? @db.VarChar(500)
|
|
checksum String? @db.VarChar(64)
|
|
version Int @default(1)
|
|
isTemplate Boolean @default(false)
|
|
isPublic Boolean @default(false)
|
|
metadata Json? // Custom metadata fields
|
|
tags String? @db.Text
|
|
folderId Int?
|
|
subfolderId Int?
|
|
createdAt DateTime @default(now()) @db.DateTime(0)
|
|
updatedAt DateTime @updatedAt @db.DateTime(0)
|
|
createdBy Int
|
|
status String @default("active") @db.VarChar(50)
|
|
|
|
folder Folder? @relation("FolderDocuments", fields: [folderId], references: [id], onDelete: SetNull)
|
|
subfolder Subfolder? @relation("SubfolderDocuments", fields: [subfolderId], references: [id], onDelete: SetNull)
|
|
user user @relation("DocumentCreator", fields: [createdBy], references: [userID])
|
|
accessRequests AccessRequest[]
|
|
permissions AccessPermission[]
|
|
versions DocumentVersion[]
|
|
|
|
@@index([folderId])
|
|
@@index([subfolderId])
|
|
@@index([createdBy])
|
|
@@index([fileType])
|
|
@@index([status])
|
|
@@fulltext([name, description, tags])
|
|
}
|
|
```
|
|
|
|
#### Access Control & Workflow
|
|
```prisma
|
|
model AccessPermission {
|
|
id Int @id @default(autoincrement())
|
|
userId Int?
|
|
roleId Int?
|
|
documentId Int?
|
|
cabinetId Int?
|
|
drawerId Int?
|
|
folderId Int?
|
|
subfolderId Int?
|
|
permissionLevel String @db.VarChar(50) // view, download, print, edit, full, admin
|
|
conditions Json? // Additional permission conditions
|
|
createdAt DateTime @default(now()) @db.DateTime(0)
|
|
updatedAt DateTime @updatedAt @db.DateTime(0)
|
|
expiresAt DateTime? @db.DateTime(0)
|
|
createdBy Int?
|
|
|
|
user user? @relation(fields: [userId], references: [userID], onDelete: SetNull)
|
|
role role? @relation(fields: [roleId], references: [roleID], onDelete: SetNull)
|
|
document Document? @relation(fields: [documentId], references: [id], onDelete: Cascade)
|
|
cabinet Cabinet? @relation(fields: [cabinetId], references: [id], onDelete: Cascade)
|
|
drawer Drawer? @relation(fields: [drawerId], references: [id], onDelete: Cascade)
|
|
folder Folder? @relation(fields: [folderId], references: [id], onDelete: Cascade)
|
|
subfolder Subfolder? @relation(fields: [subfolderId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([userId])
|
|
@@index([roleId])
|
|
@@index([documentId])
|
|
@@index([expiresAt])
|
|
}
|
|
|
|
model AccessRequest {
|
|
id Int @id @default(autoincrement())
|
|
documentId Int
|
|
userId Int
|
|
requestedLevel String @db.VarChar(50) // view, download, print, edit, full
|
|
duration String? @db.VarChar(50) // 7d, 14d, 30d, 60d, 90d, permanent
|
|
justification String? @db.Text
|
|
status String @default("pending") @db.VarChar(50) // pending, approved, rejected, expired
|
|
responseNote String? @db.Text
|
|
requestedAt DateTime @default(now()) @db.DateTime(0)
|
|
respondedAt DateTime? @db.DateTime(0)
|
|
respondedBy Int?
|
|
expiresAt DateTime? @db.DateTime(0)
|
|
|
|
document Document @relation(fields: [documentId], references: [id], onDelete: Cascade)
|
|
user user @relation(fields: [userId], references: [userID])
|
|
|
|
@@index([documentId])
|
|
@@index([userId])
|
|
@@index([status])
|
|
}
|
|
```
|
|
|
|
## Installation & Setup
|
|
|
|
### Prerequisites
|
|
- Node.js 18+ and npm/yarn/pnpm
|
|
- MySQL 8.0+ or PostgreSQL 13+
|
|
- Git for version control
|
|
- Optional: Docker for containerized deployment
|
|
|
|
### Installation Steps
|
|
|
|
1. **Clone Repository**
|
|
```bash
|
|
git clone <repository-url>
|
|
cd edms
|
|
```
|
|
|
|
2. **Install Dependencies**
|
|
```bash
|
|
# Using npm
|
|
npm install
|
|
|
|
# Using yarn
|
|
yarn install
|
|
|
|
# Using pnpm
|
|
pnpm install
|
|
```
|
|
|
|
3. **Environment Configuration**
|
|
Create `.env` file:
|
|
```env
|
|
# Database Configuration
|
|
DATABASE_URL="mysql://username:password@localhost:3306/edms_db"
|
|
# For PostgreSQL: DATABASE_URL="postgresql://username:password@localhost:5432/edms_db"
|
|
|
|
# Authentication
|
|
JWT_SECRET="your-jwt-secret-key-min-256-bits"
|
|
SESSION_SECRET="your-session-secret-key"
|
|
|
|
# External Authentication (Optional)
|
|
AUTHENTIK_BASE_URL="https://auth.yourdomain.com"
|
|
AUTHENTIK_CLIENT_ID="your-client-id"
|
|
AUTHENTIK_CLIENT_SECRET="your-client-secret"
|
|
|
|
# File Storage
|
|
UPLOAD_PATH="/var/uploads/edms"
|
|
TEMP_PATH="/var/tmp/edms"
|
|
MAX_FILE_SIZE="104857600" # 100MB in bytes
|
|
ALLOWED_FILE_TYPES="pdf,doc,docx,xls,xlsx,ppt,pptx,txt,jpg,jpeg,png,gif"
|
|
|
|
# Application Configuration
|
|
NUXT_SECRET_KEY="your-nuxt-app-secret"
|
|
BASE_URL="http://localhost:3000"
|
|
NODE_ENV="development"
|
|
|
|
# Security
|
|
CORS_ORIGIN="http://localhost:3000"
|
|
RATE_LIMIT_MAX="100" # requests per window
|
|
RATE_LIMIT_WINDOW="15" # minutes
|
|
|
|
# Monitoring & Logging
|
|
LOG_LEVEL="info"
|
|
LOG_FILE="/var/log/edms/app.log"
|
|
```
|
|
|
|
4. **Database Setup**
|
|
```bash
|
|
# Generate Prisma client
|
|
npx prisma generate
|
|
|
|
# Run database migrations
|
|
npx prisma db push
|
|
|
|
# Seed initial data (optional)
|
|
npx prisma db seed
|
|
|
|
# View database in Prisma Studio (optional)
|
|
npx prisma studio
|
|
```
|
|
|
|
5. **Development Server**
|
|
```bash
|
|
# Start development server
|
|
npm run dev
|
|
|
|
# Access application at http://localhost:3000
|
|
```
|
|
|
|
## Development Environment
|
|
|
|
### Project Structure
|
|
```
|
|
edms/
|
|
├── components/ # Vue components
|
|
│ ├── dms/ # DMS-specific components
|
|
│ │ ├── explorer/ # Document explorer interface
|
|
│ │ ├── dialogs/ # Modal dialogs and forms
|
|
│ │ ├── viewers/ # Document preview components
|
|
│ │ └── navigation/ # Tree and breadcrumb navigation
|
|
│ ├── layouts/ # Layout components
|
|
│ ├── formkit/ # FormKit custom components
|
|
│ └── [Rs*].vue # Reusable UI components (RsButton, RsCard, etc.)
|
|
├── pages/ # Nuxt pages (file-based routing)
|
|
│ ├── dms/ # DMS-related pages
|
|
│ ├── dashboard/ # Dashboard and analytics
|
|
│ ├── login/ # Authentication pages
|
|
│ └── index.vue # Homepage
|
|
├── stores/ # Pinia state stores
|
|
│ ├── dms.js # Document management store
|
|
│ ├── auth.js # Authentication store
|
|
│ └── app.js # Global application store
|
|
├── middleware/ # Route middleware
|
|
│ ├── auth.js # Authentication middleware
|
|
│ └── rbac.js # Role-based access control
|
|
├── layouts/ # Nuxt layouts
|
|
│ ├── default.vue # Default application layout
|
|
│ └── auth.vue # Authentication layout
|
|
├── server/ # Server-side code
|
|
│ ├── api/ # API routes
|
|
│ └── middleware/ # Server middleware
|
|
├── prisma/ # Database configuration
|
|
│ ├── schema.prisma # Database schema
|
|
│ └── migrations/ # Database migrations
|
|
├── public/ # Static assets
|
|
├── assets/ # Build-time assets
|
|
├── plugins/ # Nuxt plugins
|
|
├── composables/ # Vue composables
|
|
└── utils/ # Utility functions
|
|
```
|
|
|
|
### Component Architecture
|
|
|
|
#### Core EDMS Components
|
|
1. **DMSExplorer.vue**: Main document browser with tree navigation, content views, and details panel
|
|
2. **DMSTreeView.vue**: Recursive tree navigation for hierarchical structure
|
|
3. **DMSDocumentViewer.vue**: Multi-format document viewer with zoom and controls
|
|
4. **DMSAccessRequestDialog.vue**: Access request form with approval workflow
|
|
5. **DMSUploadDialog.vue**: File upload interface with metadata assignment
|
|
|
|
#### Custom UI Components (Rs Prefix)
|
|
- **RsButton.vue**: Standardized button component with variants
|
|
- **RsCard.vue**: Container component with consistent styling
|
|
- **RsModal.vue**: Modal dialog base component
|
|
- **RsTable.vue**: Data table with sorting and filtering
|
|
- **RsDropdown.vue**: Dropdown menu component
|
|
|
|
### State Management (Pinia)
|
|
|
|
#### DMS Store (`stores/dms.js`)
|
|
```javascript
|
|
export const useDmsStore = defineStore('dms', {
|
|
state: () => ({
|
|
// Navigation state
|
|
currentPath: '/',
|
|
pathHistory: [],
|
|
currentItems: [],
|
|
selectedItem: null,
|
|
|
|
// UI state
|
|
treeExpanded: {},
|
|
searchQuery: '',
|
|
viewMode: 'list', // list, grid, details
|
|
sortOrder: 'asc',
|
|
sortField: 'name',
|
|
activeTab: 'all', // all, public, private, personal
|
|
|
|
// Loading states
|
|
isLoading: false,
|
|
isUploading: false,
|
|
uploadProgress: 0,
|
|
|
|
// Dialog states
|
|
showUploadDialog: false,
|
|
showAccessRequestDialog: false,
|
|
accessRequestItem: null
|
|
}),
|
|
|
|
actions: {
|
|
// Navigation actions
|
|
async navigateTo(path) {
|
|
this.isLoading = true
|
|
try {
|
|
this.pathHistory.push(this.currentPath)
|
|
this.currentPath = path
|
|
await this.loadItems()
|
|
} finally {
|
|
this.isLoading = false
|
|
}
|
|
},
|
|
|
|
async loadItems() {
|
|
const response = await $fetch('/api/dms/items', {
|
|
query: {
|
|
path: this.currentPath,
|
|
search: this.searchQuery,
|
|
tab: this.activeTab
|
|
}
|
|
})
|
|
this.currentItems = response.items
|
|
},
|
|
|
|
// File operations
|
|
async uploadFile(file, metadata) {
|
|
this.isUploading = true
|
|
try {
|
|
const formData = new FormData()
|
|
formData.append('file', file)
|
|
formData.append('metadata', JSON.stringify(metadata))
|
|
formData.append('path', this.currentPath)
|
|
|
|
const response = await $fetch('/api/dms/upload', {
|
|
method: 'POST',
|
|
body: formData,
|
|
onUploadProgress: (progress) => {
|
|
this.uploadProgress = Math.round((progress.loaded / progress.total) * 100)
|
|
}
|
|
})
|
|
|
|
await this.loadItems()
|
|
return response
|
|
} finally {
|
|
this.isUploading = false
|
|
this.uploadProgress = 0
|
|
}
|
|
},
|
|
|
|
async requestAccess(item, requestData) {
|
|
return await $fetch('/api/dms/access-request', {
|
|
method: 'POST',
|
|
body: {
|
|
documentId: item.id,
|
|
requestedLevel: requestData.accessType,
|
|
duration: requestData.duration,
|
|
justification: requestData.justification
|
|
}
|
|
})
|
|
},
|
|
|
|
async searchDocuments(query) {
|
|
this.searchQuery = query
|
|
await this.loadItems()
|
|
}
|
|
},
|
|
|
|
getters: {
|
|
filteredItems: (state) => {
|
|
let items = state.currentItems
|
|
|
|
// Apply tab filter
|
|
if (state.activeTab !== 'all') {
|
|
items = items.filter(item => item.accessType === state.activeTab)
|
|
}
|
|
|
|
// Apply search filter
|
|
if (state.searchQuery) {
|
|
items = items.filter(item =>
|
|
item.name.toLowerCase().includes(state.searchQuery.toLowerCase()) ||
|
|
item.description?.toLowerCase().includes(state.searchQuery.toLowerCase())
|
|
)
|
|
}
|
|
|
|
// Apply sorting
|
|
items.sort((a, b) => {
|
|
const aVal = a[state.sortField]
|
|
const bVal = b[state.sortField]
|
|
const modifier = state.sortOrder === 'asc' ? 1 : -1
|
|
return aVal < bVal ? -modifier : aVal > bVal ? modifier : 0
|
|
})
|
|
|
|
return items
|
|
},
|
|
|
|
currentBreadcrumbs: (state) => {
|
|
const parts = state.currentPath.split('/').filter(Boolean)
|
|
return parts.map((part, index) => ({
|
|
name: part,
|
|
path: '/' + parts.slice(0, index + 1).join('/')
|
|
}))
|
|
},
|
|
|
|
canNavigateBack: (state) => state.pathHistory.length > 0
|
|
},
|
|
|
|
persist: {
|
|
storage: persistedState.localStorage,
|
|
pick: ['treeExpanded', 'viewMode', 'sortOrder', 'sortField']
|
|
}
|
|
})
|
|
```
|
|
|
|
## API & Data Management
|
|
|
|
### Server API Routes
|
|
|
|
#### Document Management
|
|
```javascript
|
|
// server/api/dms/documents/[id].get.js
|
|
export default defineEventHandler(async (event) => {
|
|
const id = getRouterParam(event, 'id')
|
|
// Fetch document with access control
|
|
})
|
|
|
|
// server/api/dms/upload.post.js
|
|
export default defineEventHandler(async (event) => {
|
|
// Handle file upload with validation
|
|
})
|
|
|
|
// server/api/dms/search.get.js
|
|
export default defineEventHandler(async (event) => {
|
|
const query = getQuery(event)
|
|
// Perform document search
|
|
})
|
|
```
|
|
|
|
#### Access Control
|
|
```javascript
|
|
// server/api/dms/access-request.post.js
|
|
export default defineEventHandler(async (event) => {
|
|
// Process access requests
|
|
})
|
|
|
|
// server/api/dms/permissions/[id].get.js
|
|
export default defineEventHandler(async (event) => {
|
|
// Get user permissions for item
|
|
})
|
|
```
|
|
|
|
### Data Validation
|
|
|
|
#### Prisma Schema Validation
|
|
- Field constraints and types
|
|
- Relationship integrity
|
|
- Index optimization
|
|
|
|
#### Frontend Validation
|
|
```javascript
|
|
// FormKit validation rules
|
|
const documentValidation = {
|
|
title: 'required|length:3,255',
|
|
description: 'length:0,1000',
|
|
fileType: 'required|in:pdf,doc,docx,xls,xlsx,jpg,png',
|
|
accessLevel: 'required|in:public,private,personal'
|
|
}
|
|
```
|
|
|
|
## Security & Authentication
|
|
|
|
### Authentication Flow
|
|
1. **User Login**: Redirect to Authentik
|
|
2. **Token Exchange**: Receive JWT token
|
|
3. **Token Validation**: Verify on each request
|
|
4. **Session Management**: Store in secure cookie
|
|
5. **Token Refresh**: Automatic renewal
|
|
|
|
### Authorization Levels
|
|
- **System Admin**: Full system access
|
|
- **Department Admin**: Department-wide permissions
|
|
- **Document Owner**: Full access to owned documents
|
|
- **User**: Access based on granted permissions
|
|
|
|
### File Security
|
|
- **Path Traversal Protection**: Validate file paths
|
|
- **File Type Validation**: Whitelist allowed formats
|
|
- **Size Limits**: Prevent large file uploads
|
|
- **Virus Scanning**: Optional integration
|
|
- **Access Logging**: Track file access
|
|
|
|
### Data Protection
|
|
- **SQL Injection Prevention**: Parameterized queries via Prisma
|
|
- **XSS Protection**: Input sanitization
|
|
- **CSRF Protection**: Token validation
|
|
- **Secure Headers**: Security-focused HTTP headers
|
|
|
|
## Deployment
|
|
|
|
### Production Build
|
|
```bash
|
|
# Build for production
|
|
npm run build
|
|
|
|
# Generate static files (if needed)
|
|
npm run generate
|
|
|
|
# Preview production build
|
|
npm run preview
|
|
```
|
|
|
|
### Environment Configuration
|
|
|
|
#### Production Environment Variables
|
|
```env
|
|
NODE_ENV=production
|
|
DATABASE_URL="mysql://prod_user:password@db.server:3306/edms_prod"
|
|
NUXT_SECRET_KEY="production-secret-key"
|
|
BASE_URL="https://dms.jkr-kotabharu.gov.my"
|
|
```
|
|
|
|
#### Docker Deployment
|
|
```dockerfile
|
|
FROM node:18-alpine
|
|
|
|
WORKDIR /app
|
|
COPY package*.json ./
|
|
RUN npm ci --only=production
|
|
|
|
COPY . .
|
|
RUN npm run build
|
|
|
|
EXPOSE 3000
|
|
CMD ["npm", "run", "start"]
|
|
```
|
|
|
|
### Nginx Configuration
|
|
```nginx
|
|
server {
|
|
listen 80;
|
|
server_name dms.jkr-kotabharu.gov.my;
|
|
|
|
location / {
|
|
proxy_pass http://localhost:3000;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
}
|
|
|
|
location /uploads/ {
|
|
alias /var/www/uploads/;
|
|
expires 1y;
|
|
}
|
|
}
|
|
```
|
|
|
|
## Maintenance & Monitoring
|
|
|
|
### Database Maintenance
|
|
```sql
|
|
-- Regular maintenance queries
|
|
OPTIMIZE TABLE document;
|
|
ANALYZE TABLE access_permission;
|
|
|
|
-- Cleanup old access requests
|
|
DELETE FROM access_request
|
|
WHERE status = 'rejected'
|
|
AND requested_at < DATE_SUB(NOW(), INTERVAL 30 DAY);
|
|
```
|
|
|
|
### Backup Strategy
|
|
- **Database Backups**: Daily automated backups
|
|
- **File Storage Backups**: Incremental file backups
|
|
- **Configuration Backups**: Version control for configs
|
|
|
|
### Monitoring
|
|
- **Application Performance**: Response times, error rates
|
|
- **Database Performance**: Query performance, connection counts
|
|
- **File Storage**: Disk usage, file integrity
|
|
- **Security Events**: Failed logins, access violations
|
|
|
|
### Logging
|
|
```javascript
|
|
// Server-side logging
|
|
import { createLogger } from 'winston'
|
|
|
|
const logger = createLogger({
|
|
level: 'info',
|
|
format: winston.format.json(),
|
|
transports: [
|
|
new winston.transports.File({ filename: 'error.log', level: 'error' }),
|
|
new winston.transports.File({ filename: 'combined.log' })
|
|
]
|
|
})
|
|
```
|
|
|
|
## Extension & Customization
|
|
|
|
### Adding New Document Types
|
|
1. Update Prisma schema with new file types
|
|
2. Add validation rules for the file type
|
|
3. Implement viewer component for the format
|
|
4. Update upload dialog to support the type
|
|
|
|
### Custom Access Control
|
|
1. Extend AccessPermission model
|
|
2. Implement custom permission logic
|
|
3. Update UI to reflect new permissions
|
|
4. Add admin interface for permission management
|
|
|
|
### Integration Points
|
|
- **External Authentication**: LDAP/Active Directory
|
|
- **Document Processing**: OCR, text extraction
|
|
- **Workflow Systems**: Approval workflows
|
|
- **Notification Systems**: Email/SMS notifications
|
|
- **Analytics**: Document usage analytics
|
|
|
|
### API Extensions
|
|
```javascript
|
|
// Custom API endpoint example
|
|
// server/api/custom/analytics.get.js
|
|
export default defineEventHandler(async (event) => {
|
|
// Custom analytics logic
|
|
return {
|
|
totalDocuments: count,
|
|
storageUsed: size,
|
|
activeUsers: userCount
|
|
}
|
|
})
|
|
```
|
|
|
|
### Theme Customization
|
|
```javascript
|
|
// tailwind.config.js extensions
|
|
module.exports = {
|
|
theme: {
|
|
extend: {
|
|
colors: {
|
|
primary: {
|
|
50: '#eff6ff',
|
|
500: '#3b82f6',
|
|
900: '#1e3a8a'
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
**Document Version**: 2.0
|
|
**Last Updated**: December 2024
|
|
**System Version**: EDMS v1.0
|
|
**Technology Stack**: Nuxt.js 3, Vue.js 3, Prisma, TailwindCSS
|
|
**Database**: MySQL/PostgreSQL
|
|
**Authentication**: JWT with optional external providers |