- Technology Stack: Java 11, Spring Boot 2.7.18, Maven
- Main Class:
com.isianpadu.mywira.Application.java
- Deployment: WAR packaging for Tomcat deployment
- Context Path:
/api
- Primary Database: PostgreSQL (MyWira)
- Host:
172.16.50.71:6432
- Database:
mywira
/mywiraprod
- Connection Pool: HikariCP (max 500 connections)
- Host:
- Secondary Database: PostgreSQL (LCMS)
- Host:
172.16.50.223:5432
- Database:
ltat_lcms
- Purpose: Legacy Contribution Management System integration
- Host:
- Technology: EhCache 2.10.6
- Configuration: XML-based cache configuration
- Purpose: Performance optimization for frequently accessed data
- Technology: Custom Spring Boot AOP-based solution
- Database: PostgreSQL table
app_user_activity_logs
- Features: Non-blocking, batch processing, circuit breaker pattern
- Configuration: Configurable batch size (100), flush interval (30s), queue capacity (10,000)
- Purpose: Comprehensive user activity tracking and audit trail
- Document Management: Laserfiche integration
- URL:
https://mywiraportal.ltat.gov.my/lf/api/v1/upload
- Purpose: Document storage and management
- URL:
graph LR
A[Frontend Web Portal] --> B[Spring Boot Backend]
B --> C[PostgreSQL - MyWira DB]
B --> D[PostgreSQL - LCMS DB]
B --> E[EhCache]
B --> F[Laserfiche DMS]
B --> G[Activity Logging System]
B --> H[Email Service]
B --> I[SMS Gateway - 360.my]
B --> J[LCMS API]
G --> C
G --> K[In-Memory Queue]
G --> L[Batch Processor]
style A fill:#1a237e,color:#ffffff
style B fill:#4a148c,color:#ffffff
style C fill:#1b5e20,color:#ffffff
style D fill:#1b5e20,color:#ffffff
style E fill:#e65100,color:#ffffff
style F fill:#880e4f,color:#ffffff
style G fill:#9c27b0,color:#ffffff
style H fill:#004d40,color:#ffffff
style I fill:#004d40,color:#ffffff
style J fill:#f57f17,color:#ffffff
style K fill:#ff5722,color:#ffffff
style L fill:#ff9800,color:#ffffff
sequenceDiagram
participant U as User
participant F as Auth Filter
participant J as JWT Utils
participant A as Auth Provider
participant S as User Details Service
participant D as Database
U->>F: HTTP Request with JWT
F->>J: Validate JWT Token
J->>J: Parse & Verify Signature
alt Valid Token
J->>F: Token Valid
F->>A: Create Authentication
A->>S: Load User Details
S->>D: Query User Data
D->>S: User Info
S->>A: User Details
A->>F: Authentication Success
F->>U: Continue Request
else Invalid Token
J->>F: Token Invalid
F->>U: 401 Unauthorized
end
graph TD
A[Controller] --> B{Request Type}
B -->|Command| C[Command Handler]
B -->|Query| D[Query Handler]
C --> E[Service Layer]
D --> E
E --> F[Repository Layer]
F --> G[Database]
E --> H[Event Processing]
H --> I[Scheduler Services]
I --> J[External APIs]
subgraph "Commands (Write Operations)"
C1[Create Contribution]
C2[Submit Withdrawal]
C3[Update Profile]
end
subgraph "Queries (Read Operations)"
Q1[Get Account Statement]
Q2[List Contributions]
Q3[Dashboard Data]
end
C --> C1
C --> C2
C --> C3
D --> Q1
D --> Q2
D --> Q3
style C fill:#c62828,color:#ffffff
style D fill:#2e7d32,color:#ffffff
style E fill:#6a1b9a,color:#ffffff
style I fill:#e65100,color:#ffffff
sequenceDiagram
participant U as User
participant C as Controller
participant V as Validator
participant S as Upload Service
participant L as Laserfiche
participant D as Database
U->>C: Upload File Request
C->>V: Validate File Type & Size
V->>C: Validation Result
alt Valid File
C->>S: Process Upload
S->>L: Upload to Laserfiche
L->>S: Return Document ID & URL
S->>D: Save File Metadata
D->>S: Confirmation
S->>C: Upload Success
C->>U: Document ID & Metadata
else Invalid File
C->>U: 400 Bad Request
end
graph TB
A[Business Method with @UserLogActivity] --> B[ActivityLoggingAspect]
B --> C[ActivityEvent]
C --> D[ActivityLogService]
D --> E[In-Memory Queue]
E --> F[Batch Processor]
F --> G[UserActivityLogRepository]
G --> H[PostgreSQL Database]
I[Health Check] --> D
J[REST API] --> K[Activity Log Controller]
K --> G
D --> L[Circuit Breaker]
D --> M[Metrics & Monitoring]
subgraph "Activity Types"
AT1[LOGIN/LOGOUT]
AT2[API_CALL]
AT3[DATA_CREATE/UPDATE/DELETE]
AT4[FILE_UPLOAD/DOWNLOAD]
AT5[CONTRIBUTION_SUBMIT]
AT6[WITHDRAWAL_SUBMIT]
end
B --> AT1
B --> AT2
B --> AT3
B --> AT4
B --> AT5
B --> AT6
style A fill:#1a237e,color:#ffffff
style B fill:#4a148c,color:#ffffff
style D fill:#9c27b0,color:#ffffff
style E fill:#ff5722,color:#ffffff
style F fill:#ff9800,color:#ffffff
style H fill:#1b5e20,color:#ffffff
style L fill:#d32f2f,color:#ffffff
style M fill:#00acc1,color:#ffffff
graph TD
A[Scheduler Service] --> B{Sync Type}
B -->|Contributor Data| C[LCMS API Call]
B -->|Account Statement| D[LCMS API Call]
B -->|Contribution History| E[LCMS API Call]
C --> F[Process Response]
D --> F
E --> F
F --> G[Transform Data]
G --> H[Validate Data]
H --> I[Update Local Database]
I --> J[Audit Log]
J --> K[Send Notifications]
J --> AL[Activity Logging]
subgraph "Scheduled Jobs"
S1[Daily Sync]
S2[Hourly Sync]
S3[Real-time Sync]
end
A --> S1
A --> S2
A --> S3
style A fill:#e65100,color:#ffffff
style F fill:#2e7d32,color:#ffffff
style I fill:#1a237e,color:#ffffff
style K fill:#880e4f,color:#ffffff
style AL fill:#9c27b0,color:#ffffff
- Primary URL:
http://172.16.50.221:8080/lcms-platform
- Fallback URL:
http://172.16.50.222:8080/lcms-platform
- Authentication: Bearer Token (JWT)
- Purpose: Contributor data synchronization, account statements, contributions
- Email Configuration:
- From Address:
[email protected]
- Personal Name:
MyWIRA
- From Address:
- SMTP Provider: Configuration managed via profile-specific properties
- Backup SMTP: Office365 SMTP (configured in mail.properties)
- Host:
smtp.office365.com
- From Address:
[email protected]
- Host:
- Provider: 360.my SMS Gateway
- URL:
https://sms.360.my/gw/bulk360/v3_0/send.php
- Sender ID:
MyWIRA
- Backup Provider: GoSMS
- URL:
https://api.gosms.com.my/eapi/sms.aspx
- URL:
- Laserfiche: Document management system integration
- Frontend URL:
https://mywiraportal.ltat.gov.my
- Purpose: User interface for the MyWira portal
graph TB
subgraph "Frontend Layer"
FE[Web Portal - Angular/React]
end
subgraph "API Gateway Layer"
AG[Spring Boot Controllers]
AG1[Auth Controller]
AG2[Contribution Controller]
AG3[Withdrawal Controller]
AG4[Dashboard Controller]
end
subgraph "Business Logic Layer"
BL1[User Service]
BL2[Contribution Service]
BL3[Withdrawal Service]
BL4[Notification Service]
BL5[Dashboard Service]
BL6[Activity Log Service]
end
subgraph "Data Access Layer"
DA1[User Repository]
DA2[Contribution Repository]
DA3[Withdrawal Repository]
DA4[Audit Repository]
DA5[Activity Log Repository]
end
subgraph "External Integrations"
EI1[LCMS API]
EI2[Email Service]
EI3[SMS Gateway]
EI4[Laserfiche]
end
subgraph "Database Layer"
DB1[(MyWira DB)]
DB2[(LCMS DB)]
end
FE --> AG
AG --> AG1
AG --> AG2
AG --> AG3
AG --> AG4
AG1 --> BL1
AG2 --> BL2
AG3 --> BL3
AG4 --> BL5
BL1 --> DA1
BL2 --> DA2
BL3 --> DA3
BL4 --> DA4
BL6 --> DA5
BL1 --> BL6
BL2 --> BL6
BL3 --> BL6
BL2 --> EI1
BL3 --> EI1
BL4 --> EI2
BL4 --> EI3
BL3 --> EI4
DA1 --> DB1
DA2 --> DB1
DA3 --> DB1
DA4 --> DB1
DA5 --> DB1
EI1 --> DB2
style FE fill:#1a237e,color:#ffffff
style AG fill:#4a148c,color:#ffffff
style BL1 fill:#2e7d32,color:#ffffff
style BL2 fill:#2e7d32,color:#ffffff
style BL3 fill:#2e7d32,color:#ffffff
style BL4 fill:#2e7d32,color:#ffffff
style BL5 fill:#2e7d32,color:#ffffff
style BL6 fill:#9c27b0,color:#ffffff
style EI1 fill:#f57f17,color:#ffffff
style EI2 fill:#004d40,color:#ffffff
style EI3 fill:#004d40,color:#ffffff
style EI4 fill:#880e4f,color:#ffffff
sequenceDiagram
participant U as User
participant P as Portal
participant API as MyWira API
participant LCMS as LCMS System
participant EMAIL as Email Service
participant SMS as SMS Gateway
Note over U,SMS: User Registration Flow
U->>P: Register Account
P->>API: Submit Registration
API->>LCMS: Validate Contributor
LCMS->>API: Contributor Data
API->>EMAIL: Send Verification Email
API->>SMS: Send OTP SMS
EMAIL->>U: Email Notification
SMS->>U: SMS OTP
Note over U,SMS: Contribution Submission
U->>P: Submit Contribution
P->>API: Process Contribution
API->>LCMS: Sync Contribution Data
LCMS->>API: Confirmation
API->>EMAIL: Send Confirmation
EMAIL->>U: Success Notification
graph TB
subgraph "Client Layer"
CL[Web Browser]
MOBILE[Mobile App]
end
subgraph "Network Security"
HTTPS[HTTPS/TLS 1.3]
CORS[CORS Policy]
FIREWALL[Network Firewall]
end
subgraph "Application Security"
AUTH[Authentication Filter]
JWT[JWT Validation]
RBAC[Role-Based Access Control]
RATE[Rate Limiting]
VALID[Input Validation]
CSRF[CSRF Protection]
end
subgraph "Data Security"
ENCRYPT[Data Encryption]
HASH[Password Hashing - BCrypt]
AUDIT[Audit Logging]
BACKUP[Secure Backups]
end
subgraph "Infrastructure Security"
DB_SEC[Database Security]
FILE_SEC[File Upload Security]
API_SEC[API Security]
MONITOR[Security Monitoring]
end
CL --> HTTPS
MOBILE --> HTTPS
HTTPS --> FIREWALL
FIREWALL --> CORS
CORS --> AUTH
AUTH --> JWT
JWT --> RBAC
RBAC --> RATE
RATE --> VALID
VALID --> CSRF
CSRF --> ENCRYPT
ENCRYPT --> HASH
HASH --> AUDIT
AUDIT --> DB_SEC
DB_SEC --> FILE_SEC
FILE_SEC --> API_SEC
API_SEC --> MONITOR
style CL fill:#1a237e,color:#ffffff
style HTTPS fill:#2e7d32,color:#ffffff
style AUTH fill:#e65100,color:#ffffff
style JWT fill:#e65100,color:#ffffff
style RBAC fill:#e65100,color:#ffffff
style ENCRYPT fill:#880e4f,color:#ffffff
style HASH fill:#880e4f,color:#ffffff
style DB_SEC fill:#4a148c,color:#ffffff
sequenceDiagram
participant U as User
participant F as Frontend
participant A as Auth Controller
participant J as JWT Service
participant P as Auth Provider
participant S as User Service
participant D as Database
Note over U,D: Login Process
U->>F: Enter Credentials
F->>A: POST /auth/login
A->>P: Authenticate User
P->>S: Load User Details
S->>D: Query User & Roles
D->>S: User Data with Permissions
S->>P: UserDetails Object
P->>A: Authentication Success
A->>J: Generate JWT Token
J->>A: JWT with Claims
A->>F: JWT Token + User Info
F->>U: Login Success
Note over U,D: Authenticated Request
U->>F: Make API Request
F->>A: Request with JWT Header
A->>J: Validate JWT
J->>J: Check Signature & Expiry
J->>A: Token Valid
A->>S: Authorize Request
S->>A: Permission Granted
A->>F: API Response
F->>U: Display Data
- JWT-based Authentication: Token expiration set to 24 hours (86400000ms)
- Multiple Authentication Providers: Support for standard and MyDID authentication
- Password Security: BCrypt encryption with strength 10
- Session Management: Stateless (no server-side sessions)
- Role-Based Access Control (RBAC): Implemented through Spring Security
- Method-Level Security:
@EnableMethodSecurity
annotations - Public Endpoints: Limited set of endpoints accessible without authentication
/token
,/auth/**
,/appConfig/**
,/doc/**
,/public/**
,/metrics/**
- CORS Configuration:
- Allowed Origins: All (
*
) - Allowed Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS
- Exposed Headers: Authorization
- Max Age: 3600 seconds
- Allowed Origins: All (
- Database Security: Connection pooling with timeout configurations
- Password History: Tracks previous passwords to prevent reuse
- Password Reset: Secure token-based password reset mechanism
- Rate Limiting: Implemented via
RateLimitService
andRateLimitInterceptor
- Spring Validation: Bean validation annotations
- File Upload Security:
- Allowed file types: PDF, JPEG, PNG, JPG, GIF, BMP, WEBP
- Maximum file size: 10MB
- Maximum request size: 10MB
- Audit Trail: Comprehensive audit logging through
AuditTrailService
- User Activity Logging: Non-blocking activity tracking via
@UserLogActivity
annotation- Database Table:
app_user_activity_logs
with optimized indexes - Batch Processing: Configurable batch size (100 events) and flush interval (30s)
- Circuit Breaker: Automatic failure handling and system protection
- Activity Types: LOGIN, API_CALL, DATA_CREATE/UPDATE/DELETE, FILE_UPLOAD/DOWNLOAD, etc.
- Database Table:
- HTTP Request Logging: Via
HttpLoggingInterceptor
andUserLoggingFilter
- Structured Logging: Logback configuration with environment-specific settings
- Authentication Filter:
AuthTokenFilter
for JWT validation - CSRF Protection: Disabled for REST API (stateless)
- Security Entry Point: Custom authentication entry point for unauthorized access
- Configuration Management: Environment-specific property files
- Secret Management: JWT secrets and API keys stored in configuration
- Data Encryption: Utility classes for encryption operations
- Scheduled Jobs:
- Account unlock scheduler
- Email/SMS processing
- Data synchronization with LCMS
- Auto-resolved inquiry processing
- Async Processing: Enabled for background tasks
- Job Execution Logging: Tracks scheduled job execution
# | Component | Internal IP | Public IP (via CDN) | Hostname | Network Zone |
---|---|---|---|---|---|
1 | External Load Balancer | 172.30.10.68 | 104.26.0.87, 104.26.1.87, 172.67.74.126 | mywprod-extloadbalancer | DMZ |
2 | Portal Database | 172.16.50.71 | N/A (Internal Only) | mywprod-portaldb | VLAN50 |
3 | Portal Server 1 | 172.30.10.69 | Via Load Balancer | mywprod-portalserver1 | DMZ |
4 | Portal Server 2 | 172.30.10.70 | Via Load Balancer | mywprod-portalserver2 | DMZ |
Note: The public IPs (104.26.0.87, 104.26.1.87, 172.67.74.126) are managed through Cloudflare CDN for mywiraportal.ltat.gov.my
graph TB
subgraph "Internet & CDN"
INTERNET[Internet Users]
CDN[Cloudflare CDN<br/>104.26.0.87<br/>104.26.1.87<br/>172.67.74.126]
end
subgraph "DMZ Network - 172.30.10.0/24"
ELB[External Load Balancer<br/>172.30.10.68]
PS1[Portal Server 1<br/>172.30.10.69]
PS2[Portal Server 2<br/>172.30.10.70]
end
subgraph "Internal Network - VLAN50 - 172.16.50.0/24"
DB[Portal Database<br/>172.16.50.71]
LCMS1[LCMS Server 1<br/>172.16.50.221]
LCMS2[LCMS Server 2<br/>172.16.50.222]
LCMS_DB[LCMS Database<br/>172.16.50.223]
end
subgraph "Security Zones"
FW1[Firewall - CDN to DMZ]
FW2[Firewall - DMZ to Internal]
end
INTERNET --> CDN
CDN --> FW1
FW1 --> ELB
ELB --> PS1
ELB --> PS2
PS1 --> FW2
PS2 --> FW2
FW2 --> DB
FW2 --> LCMS1
FW2 --> LCMS2
FW2 --> LCMS_DB
PS1 --> LCMS1
PS1 --> LCMS2
PS2 --> LCMS1
PS2 --> LCMS2
style INTERNET fill:#1a237e,color:#ffffff
style CDN fill:#e65100,color:#ffffff
style ELB fill:#f57f17,color:#ffffff
style PS1 fill:#2e7d32,color:#ffffff
style PS2 fill:#2e7d32,color:#ffffff
style DB fill:#1565c0,color:#ffffff
style LCMS1 fill:#d84315,color:#ffffff
style LCMS2 fill:#d84315,color:#ffffff
style LCMS_DB fill:#d84315,color:#ffffff
style FW1 fill:#b71c1c,color:#ffffff
style FW2 fill:#b71c1c,color:#ffffff
graph TB
subgraph "External Access"
USERS[End Users]
DNS[DNS Resolution<br/>mywiraportal.ltat.gov.my]
CDN_LAYER[Cloudflare CDN<br/>104.26.0.87, 104.26.1.87<br/>172.67.74.126]
end
subgraph "DMZ - External Load Balancer"
ELB[External Load Balancer<br/>172.30.10.68<br/>HAProxy/Nginx]
end
subgraph "DMZ - Application Servers"
AS1[Portal Server 1<br/>172.30.10.69<br/>Tomcat + Spring Boot]
AS2[Portal Server 2<br/>172.30.10.70<br/>Tomcat + Spring Boot]
end
subgraph "VLAN50 - Database Layer"
DB_PRIMARY[Primary Database<br/>172.16.50.71<br/>PostgreSQL Master]
DB_CACHE[Application Cache<br/>EhCache In-Memory]
end
subgraph "VLAN50 - External Integrations"
LCMS_PRIMARY[LCMS Primary<br/>172.16.50.221<br/>REST API Server]
LCMS_BACKUP[LCMS Backup<br/>172.16.50.222<br/>REST API Server]
LCMS_DATABASE[LCMS Database<br/>172.16.50.223<br/>PostgreSQL]
end
subgraph "External Services"
EMAIL[Email Service<br/>SMTP Providers]
SMS[SMS Gateway<br/>360.my]
LASERFICHE[Laserfiche DMS<br/>Document Storage]
end
USERS --> DNS
DNS --> CDN_LAYER
CDN_LAYER --> ELB
ELB -->|Round Robin| AS1
ELB -->|Round Robin| AS2
AS1 --> DB_PRIMARY
AS2 --> DB_PRIMARY
AS1 --> DB_CACHE
AS2 --> DB_CACHE
AS1 -->|Primary| LCMS_PRIMARY
AS1 -->|Fallback| LCMS_BACKUP
AS2 -->|Primary| LCMS_PRIMARY
AS2 -->|Fallback| LCMS_BACKUP
LCMS_PRIMARY --> LCMS_DATABASE
LCMS_BACKUP --> LCMS_DATABASE
AS1 --> EMAIL
AS1 --> SMS
AS1 --> LASERFICHE
AS2 --> EMAIL
AS2 --> SMS
AS2 --> LASERFICHE
style USERS fill:#1a237e,color:#ffffff
style CDN_LAYER fill:#e65100,color:#ffffff
style ELB fill:#f57f17,color:#ffffff
style AS1 fill:#2e7d32,color:#ffffff
style AS2 fill:#2e7d32,color:#ffffff
style DB_PRIMARY fill:#1565c0,color:#ffffff
style LCMS_PRIMARY fill:#d84315,color:#ffffff
style LCMS_BACKUP fill:#d84315,color:#ffffff
style EMAIL fill:#ad1457,color:#ffffff
style SMS fill:#ad1457,color:#ffffff
style LASERFICHE fill:#4a148c,color:#ffffff
graph TB
subgraph "Security Zones"
subgraph "Internet Zone"
INT[Internet<br/>Untrusted Network]
end
subgraph "DMZ Zone - 172.30.10.0/24"
ELB_DMZ[Load Balancer<br/>172.30.10.68]
APP1_DMZ[App Server 1<br/>172.30.10.69]
APP2_DMZ[App Server 2<br/>172.30.10.70]
end
subgraph "Internal Zone - VLAN50 - 172.16.50.0/24"
DB_INT[Database<br/>172.16.50.71]
LCMS_INT[LCMS Servers<br/>172.16.50.221-223]
end
end
subgraph "Security Controls"
subgraph "Perimeter Security"
FW_EXT[External Firewall<br/>Internet to DMZ]
FW_INT[Internal Firewall<br/>DMZ to VLAN50]
end
subgraph "Access Controls"
RULES_EXT[Rules: HTTPS/443, HTTP/80]
RULES_INT[Rules: PostgreSQL/5432, LCMS/8080]
end
subgraph "Monitoring"
IDS[Intrusion Detection]
LOG[Security Logging]
SIEM[SIEM Integration]
end
end
INT --> FW_EXT
FW_EXT --> ELB_DMZ
FW_EXT --> RULES_EXT
ELB_DMZ --> APP1_DMZ
ELB_DMZ --> APP2_DMZ
APP1_DMZ --> FW_INT
APP2_DMZ --> FW_INT
FW_INT --> RULES_INT
FW_INT --> DB_INT
FW_INT --> LCMS_INT
FW_EXT --> IDS
FW_INT --> IDS
IDS --> LOG
LOG --> SIEM
style INT fill:#c62828,color:#ffffff
style ELB_DMZ fill:#e65100,color:#ffffff
style APP1_DMZ fill:#2e7d32,color:#ffffff
style APP2_DMZ fill:#2e7d32,color:#ffffff
style DB_INT fill:#1a237e,color:#ffffff
style LCMS_INT fill:#f57f17,color:#ffffff
style FW_EXT fill:#b71c1c,color:#ffffff
style FW_INT fill:#b71c1c,color:#ffffff
style IDS fill:#e65100,color:#ffffff
graph TB
subgraph "External Load Balancer - 172.30.10.68"
NGINX[Nginx Load Balancer<br/>TLS 1.2/1.3<br/>HTTP/2 Enabled]
SSL_TERM[SSL Termination<br/>Certificate: ltat.crt<br/>Key: ltat.rsa]
SECURITY[Security Headers<br/>HSTS, X-Frame-Options<br/>XSS Protection]
end
subgraph "Upstream Configurations"
WEB_UPSTREAM[Web Upstream<br/>Angular Frontend<br/>Port 9090]
API_UPSTREAM[Backend Upstream<br/>Spring Boot API<br/>Port 8080]
LF_UPSTREAM[Laserfiche Upstream<br/>Document Server<br/>Port 9090]
end
subgraph "Backend Servers"
WEB1[Web Server 1<br/>172.30.10.69:9090<br/>Weight: 1]
WEB2[Web Server 2<br/>172.30.10.70:9090<br/>Weight: 1 - Disabled]
API1[API Server 1<br/>172.30.10.69:8080<br/>Weight: 1]
API2[API Server 2<br/>172.30.10.70:8080<br/>Weight: 1]
LF1[Laserfiche Server<br/>172.16.51.82:9090<br/>Weight: 1]
end
subgraph "Routing Rules"
ROOT_ROUTE[Root Path to Web<br/>Angular SPA]
API_ROUTE[API Gateway Route<br/>Backend API with Rewrite]
LF_ROUTE[Laserfiche Route<br/>Document Management]
HEALTH_ROUTE[Health Check Route<br/>Load Balancer Status]
end
NGINX --> SSL_TERM
SSL_TERM --> SECURITY
SECURITY --> ROOT_ROUTE
SECURITY --> API_ROUTE
SECURITY --> LF_ROUTE
SECURITY --> HEALTH_ROUTE
ROOT_ROUTE --> WEB_UPSTREAM
API_ROUTE --> API_UPSTREAM
LF_ROUTE --> LF_UPSTREAM
WEB_UPSTREAM --> WEB1
WEB_UPSTREAM -.-> WEB2
API_UPSTREAM --> API1
API_UPSTREAM --> API2
LF_UPSTREAM --> LF1
style NGINX fill:#2e7d32,color:#ffffff
style SSL_TERM fill:#c62828,color:#ffffff
style SECURITY fill:#e65100,color:#ffffff
style WEB_UPSTREAM fill:#1b5e20,color:#ffffff
style API_UPSTREAM fill:#1a237e,color:#ffffff
style LF_UPSTREAM fill:#f57f17,color:#ffffff
style WEB2 fill:#d32f2f,color:#ffffff
sequenceDiagram
participant U as User
participant CDN as Cloudflare CDN
participant LB as Nginx Load Balancer
participant WEB as Web Server (Angular)
participant API as API Server (Spring Boot)
participant LF as Laserfiche Server
Note over U,LF: Frontend Request Flow
U->>CDN: HTTPS Request (/)
CDN->>LB: Forward to 172.30.10.68:443
LB->>LB: SSL Termination & Security Headers
LB->>WEB: Proxy to Web Upstream (9090)
WEB->>LB: Angular SPA Response
LB->>CDN: HTTPS Response
CDN->>U: Cached/Optimized Response
Note over U,LF: API Request Flow
U->>CDN: HTTPS Request to API Gateway
CDN->>LB: Forward with gw prefix
LB->>LB: Rewrite gw path to root
LB->>API: Proxy to Backend Upstream (8080)
API->>LB: JSON API Response
LB->>CDN: HTTPS Response
CDN->>U: API Data
Note over U,LF: Document Upload Flow
U->>CDN: HTTPS Request to Laserfiche
CDN->>LB: Forward with lf prefix
LB->>LB: Rewrite lf path to root
LB->>LF: Proxy to Laserfiche (9090)
LF->>LB: Document Response
LB->>CDN: HTTPS Response
CDN->>U: Document/Upload Result
graph TB
subgraph "Web Server - 172.30.10.69:9090"
WEB_SERVER[Nginx Web Server<br/>Angular SPA Host]
STATIC_CACHE[Static File Caching<br/>JS, CSS, Images<br/>1 Year Expiry]
SPA_ROUTING[SPA Fallback Routing<br/>try_files $uri /index.html]
WEB_HEALTH[Health Check<br/>/health endpoint]
end
subgraph "Static Assets"
JS[JavaScript Files<br/>Gzip Compressed]
CSS[CSS Stylesheets<br/>Cache-Control: immutable]
IMAGES[Images & Fonts<br/>Long-term Caching]
end
subgraph "Angular Application"
INDEX[index.html<br/>No-cache Headers]
ROUTES[Client-side Routes<br/>Angular Router]
COMPONENTS[Angular Components<br/>Lazy Loaded]
end
WEB_SERVER --> STATIC_CACHE
WEB_SERVER --> SPA_ROUTING
WEB_SERVER --> WEB_HEALTH
STATIC_CACHE --> JS
STATIC_CACHE --> CSS
STATIC_CACHE --> IMAGES
SPA_ROUTING --> INDEX
INDEX --> ROUTES
ROUTES --> COMPONENTS
style WEB_SERVER fill:#2e7d32,color:#ffffff
style STATIC_CACHE fill:#e65100,color:#ffffff
style SPA_ROUTING fill:#1b5e20,color:#ffffff
style INDEX fill:#1a237e,color:#ffffff
style JS fill:#d84315,color:#ffffff
style CSS fill:#d84315,color:#ffffff
style IMAGES fill:#d84315,color:#ffffff
Server Details:
- Host: 172.30.10.68
- Ports: 80 (HTTP redirect), 443 (HTTPS/HTTP2)
- SSL: TLS 1.2/1.3 with custom certificates
- Security: HSTS, X-Frame-Options, XSS Protection
Upstream Configurations:
- Web Upstream: Round-robin to port 9090 (Angular frontend)
- Active: 172.30.10.69:9090
- Disabled: 172.30.10.70:9090
- Backend Upstream: Round-robin to port 8080 (Spring Boot API)
- Active: 172.30.10.69:8080, 172.30.10.70:8080
- Laserfiche Upstream: Single server
- Active: 172.16.51.82:9090
Routing Rules:
/
→ Web upstream (Angular SPA)/gw/*
→ Backend upstream (API with path rewrite)/lf/*
→ Laserfiche upstream (Document management)/nginx-health
→ Health check endpoint
Performance Settings:
- Connection pooling: 64 keepalive connections
- Client body size: 10MB
- Proxy timeouts: 30s for all operations
- HTTP/2 enabled for better performance
Server Details:
- Port: 9090
- Document Root:
/var/www/html/mwp-web
- Application: Angular Single Page Application
Caching Strategy:
- Static Assets: 1-year expiry with immutable cache control
- Files:
*.js
,*.css
,*.png
,*.jpg
,*.jpeg
,*.gif
,*.ico
,*.svg
,*.woff
,*.woff2
,*.ttf
,*.eot
- Gzip compression enabled
- Files:
- HTML Files: No-cache headers to ensure updates are delivered
- SPA Routing:
try_files $uri $uri/ /index.html
for client-side routing
Health Monitoring:
- Endpoint:
/health
- Response: "web-server-healthy"
- Logging: Disabled for health checks
sequenceDiagram
participant DEV as Developer
participant GIT as Git Repository
participant DEPLOY as Deployment Script
participant SERVER1 as Portal Server 1 (172.30.10.69)
participant SERVER2 as Portal Server 2 (172.30.10.70)
participant LB as Load Balancer (172.30.10.68)
participant HEALTH as Health Check Endpoint
Note over DEV,HEALTH: Zero-Downtime Rolling Deployment
DEV->>DEPLOY: ./deployment.sh ltat
DEPLOY->>DEPLOY: Profile Selection & Validation
DEPLOY->>SERVER1: Test SSH Connectivity
DEPLOY->>SERVER2: Test SSH Connectivity
Note over DEPLOY,HEALTH: Pre-deployment Health Check
DEPLOY->>SERVER1: Check Current Health Status
SERVER1->>HEALTH: GET /api/metrics/health
HEALTH->>DEPLOY: Server 1 Status
DEPLOY->>SERVER2: Check Current Health Status
SERVER2->>HEALTH: GET /api/metrics/health
HEALTH->>DEPLOY: Server 2 Status
Note over DEPLOY,HEALTH: Deploy to Server 1 First
DEPLOY->>SERVER1: Deploy Script Execution
SERVER1->>GIT: git pull origin main
SERVER1->>SERVER1: mvn clean package -DskipTests
SERVER1->>SERVER1: Graceful Shutdown (SIGTERM)
SERVER1->>SERVER1: Start New Instance (PID)
SERVER1->>HEALTH: Health Check Validation
HEALTH->>DEPLOY: Server 1 Healthy ✅
Note over DEPLOY,HEALTH: Wait 15s then Deploy to Server 2
DEPLOY->>DEPLOY: Sleep 15 seconds
DEPLOY->>SERVER2: Deploy Script Execution
SERVER2->>GIT: git pull origin main
SERVER2->>SERVER2: mvn clean package -DskipTests
SERVER2->>SERVER2: Graceful Shutdown (SIGTERM)
SERVER2->>SERVER2: Start New Instance (PID)
SERVER2->>HEALTH: Health Check Validation
HEALTH->>DEPLOY: Server 2 Healthy ✅
Note over DEPLOY,HEALTH: Final Status Check
DEPLOY->>SERVER1: Check Final Status
DEPLOY->>SERVER2: Check Final Status
DEPLOY->>DEV: Deployment Complete ✅
graph TB
subgraph "Deployment Environments"
UAT_ENV[UAT Environment<br/>172.30.10.72, 172.30.10.73]
LTAT_ENV[LTAT Environment<br/>172.30.10.69, 172.30.10.70]
end
subgraph "Deployment Script Components"
PROFILE[Profile Selection<br/>uat / ltat]
SSH_CHECK[SSH Connectivity Test<br/>Key-based Authentication]
PRE_CHECK[Pre-deployment Health Check<br/>Ensure Zero Downtime]
ROLLING[Rolling Deployment<br/>Server1 → Wait → Server2]
end
subgraph "Build & Deploy Process"
GIT_PULL[Git Pull<br/>origin/main]
MVN_BUILD[Maven Build<br/>clean package -DskipTests]
GRACEFUL[Graceful Shutdown<br/>SIGTERM → Wait → SIGKILL]
NEW_START[New Instance Start<br/>JVM Heap: 8GB]
HEALTH_WAIT[Health Check Wait<br/>Max 60 seconds]
end
subgraph "Monitoring & Validation"
HEALTH_ENDPOINT[Health Endpoint<br/>/api/metrics/health:8080]
LOG_TAIL[Log Monitoring<br/>app.log tail -f]
STATUS_CHECK[Process Status<br/>PID, CPU, Memory]
ROLLBACK_READY[Rollback Capability<br/>Previous Instance Recovery]
end
PROFILE --> SSH_CHECK
SSH_CHECK --> PRE_CHECK
PRE_CHECK --> ROLLING
ROLLING --> GIT_PULL
GIT_PULL --> MVN_BUILD
MVN_BUILD --> GRACEFUL
GRACEFUL --> NEW_START
NEW_START --> HEALTH_WAIT
HEALTH_WAIT --> HEALTH_ENDPOINT
HEALTH_ENDPOINT --> LOG_TAIL
LOG_TAIL --> STATUS_CHECK
STATUS_CHECK --> ROLLBACK_READY
style PROFILE fill:#1a237e,color:#ffffff
style ROLLING fill:#e65100,color:#ffffff
style GRACEFUL fill:#d84315,color:#ffffff
style HEALTH_ENDPOINT fill:#2e7d32,color:#ffffff
style ROLLBACK_READY fill:#c62828,color:#ffffff
graph LR
subgraph "Configuration Settings"
APP_CONFIG[Application Config<br/>APP_NAME: mywira-backend<br/>DEPLOY_DIR: /home/isianpadu_user3/projects/mywira-backend<br/>JAR_FILE: target/mywira-backend.war]
JVM_CONFIG[JVM Configuration<br/>HEAP_SIZE: 8GB<br/>METASPACE_SIZE: 512MB<br/>PROFILE: uat/ltat]
HEALTH_CONFIG[Health Check Config<br/>URL: /api/metrics/health<br/>PORT: 8080<br/>MAX_WAIT: 60 seconds]
end
subgraph "Environment Servers"
UAT_SERVERS[UAT Servers<br/>172.30.10.72<br/>172.30.10.73]
LTAT_SERVERS[LTAT Servers<br/>172.30.10.69<br/>172.30.10.70]
end
subgraph "SSH Configuration"
SSH_OPTS[SSH Options<br/>StrictHostKeyChecking=no<br/>ConnectTimeout=5s<br/>PasswordAuthentication=no<br/>PubkeyAuthentication=yes<br/>BatchMode=yes]
end
subgraph "Deployment Commands"
COMMANDS[Available Commands<br/>deploy profile<br/>status profile<br/>logs profile<br/>test profile<br/>uat shorthand<br/>ltat shorthand]
end
APP_CONFIG --> JVM_CONFIG
JVM_CONFIG --> HEALTH_CONFIG
HEALTH_CONFIG --> UAT_SERVERS
HEALTH_CONFIG --> LTAT_SERVERS
UAT_SERVERS --> SSH_OPTS
LTAT_SERVERS --> SSH_OPTS
SSH_OPTS --> COMMANDS
style APP_CONFIG fill:#1a237e,color:#ffffff
style JVM_CONFIG fill:#e65100,color:#ffffff
style HEALTH_CONFIG fill:#2e7d32,color:#ffffff
style SSH_OPTS fill:#d84315,color:#ffffff
style COMMANDS fill:#ad1457,color:#ffffff
- Rolling Deployment: Deploy to one server at a time
- Health Check Validation: Ensure new instance is healthy before proceeding
- Graceful Shutdown: SIGTERM followed by 30s wait, then SIGKILL if needed
- 15-second Gap: Wait between server deployments for stability
- UAT Environment: 172.30.10.72, 172.30.10.73
- LTAT Environment: 172.30.10.69, 172.30.10.70 (Production)
- Profile Selection: Interactive or command-line argument
- Local vs Remote: Detects if running locally on a server
- Git Update:
git pull origin main
- Maven Build:
mvn clean package -DskipTests
- Process Management: Find and gracefully stop old instance
- JVM Startup: Configure 8GB heap, 512MB metaspace
- Health Monitoring: Wait up to 60 seconds for health check pass
- Pre-deployment Check: Ensures at least one server is running
- SSH Connectivity Test: Validates authentication before deployment
- Process Monitoring: CPU, memory, uptime tracking
- Log Tailing: Real-time log monitoring across all servers
- Rollback Ready: Can recover previous instance if needed
# Deployment commands
./deployment.sh deploy ltat # Deploy with LTAT profile
./deployment.sh ltat # Shorthand for LTAT deployment
./deployment.sh status ltat # Check deployment status
./deployment.sh logs ltat # Tail logs from all servers
./deployment.sh test ltat # Test connectivity and health
- System Overview - High-level component relationships
- Authentication Flow - JWT-based security process
- Business Operations - CQRS pattern implementation
- File Upload Process - Multi-service integration
- Data Synchronization - Scheduled background jobs
- Module Integration - Layered architecture design
- Integration Flows - External service interactions
- Security Architecture - Multi-layered security controls
- Network Topology - Production infrastructure layout
- Nginx Configuration - Load balancer and routing setup
- Zero-Downtime Deployment - Rolling deployment strategy
The system demonstrates a robust, scalable, and secure foundation for the MyWira contribution management system, with comprehensive integration capabilities, strong security controls, and production-ready deployment processes.