Files
Nilai_Clock_Client/doc/DATABASE_SCHEMA.md
T
2025-07-11 14:07:20 +08:00

13 KiB

Database Schema Documentation - Nilai Clock Client

Overview

The Nilai Clock Client uses a MySQL 5.x database with MyISAM engine. The schema is designed for simplicity and performance, with consolidated device tracking and streamlined location updates.

Database Configuration

Connection Settings

// Backend database configuration
const dbConfig = {
  host: process.env.DB_HOST,
  user: process.env.DB_USER,
  password: process.env.DB_PASSWORD,
  database: process.env.DB_NAME,
  port: process.env.DB_PORT,
  waitForConnections: true,
  connectionLimit: 10,
  queueLimit: 0,
  // MySQL 5.x specific settings
  charset: 'utf8',
  timezone: 'local'
}

Engine and Charset

  • Engine: MyISAM (MySQL 5.x compatibility)
  • Charset: UTF-8
  • Collation: utf8_general_ci

Core Tables

workers

Primary user accounts table for both workers and managers.

CREATE TABLE `workers` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) NOT NULL,
  `password_hash` varchar(255) NOT NULL COMMENT 'Store hashed passwords, not plain text!',
  `full_name` varchar(255) NOT NULL,
  `role` enum('worker','manager') NOT NULL,
  `device_uuid` varchar(255) DEFAULT NULL,
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `username` (`username`),
  KEY `idx_device_uuid` (`device_uuid`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Stores user account information for both workers and managers';

Key Features:

  • Consolidated device tracking via device_uuid field
  • Role-based access (worker/manager)
  • Unique username constraint
  • Indexed device UUID for fast lookups

Sample Data:

INSERT INTO workers (username, password_hash, full_name, role, device_uuid) VALUES
('worker001', '$2b$10$hash...', 'John Doe', 'worker', 'android-uuid-123'),
('manager001', '$2b$10$hash...', 'Jane Smith', 'manager', NULL);

clock_records

Time tracking events with location data.

CREATE TABLE `clock_records` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `worker_id` int(11) NOT NULL,
  `event_type` enum('clock_in','clock_out','failed') NOT NULL,
  `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `qr_code_id` varchar(255) DEFAULT NULL,
  `latitude` decimal(10,8) DEFAULT NULL,
  `longitude` decimal(11,8) DEFAULT NULL,
  `notes` text,
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `idx_worker_id` (`worker_id`),
  KEY `idx_event_type` (`event_type`),
  KEY `idx_timestamp` (`timestamp`),
  KEY `idx_qr_code_id` (`qr_code_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Records of worker clock in/out events';

Key Features:

  • Links to workers table via worker_id
  • Event types: clock_in, clock_out, failed
  • Optional QR code reference
  • Precise location coordinates (8 decimal places)
  • Optional notes field for manual entries

Sample Data:

INSERT INTO clock_records (worker_id, event_type, qr_code_id, latitude, longitude) VALUES
(1, 'clock_in', 'qr-uuid-123', -6.20880000, 106.84560000),
(1, 'clock_out', 'qr-uuid-123', -6.20880000, 106.84560000);

qr_codes

QR code definitions and status.

CREATE TABLE `qr_codes` (
  `id` varchar(255) NOT NULL,
  `name` varchar(255) NOT NULL,
  `is_active` tinyint(1) NOT NULL DEFAULT '1',
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `idx_is_active` (`is_active`),
  KEY `idx_name` (`name`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='QR codes for clock in/out locations';

Key Features:

  • UUID-based primary key
  • Human-readable name
  • Active/inactive status
  • Creation timestamp

Sample Data:

INSERT INTO qr_codes (id, name, is_active) VALUES
('550e8400-e29b-41d4-a716-446655440000', 'Main Entrance', 1),
('550e8400-e29b-41d4-a716-446655440001', 'Side Gate', 1),
('550e8400-e29b-41d4-a716-446655440002', 'Warehouse Door', 0);

location_updates

Simplified location tracking for background monitoring.

CREATE TABLE `location_updates` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `longitude` decimal(11,8) NOT NULL,
  `latitude` decimal(10,8) NOT NULL,
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `idx_user_id` (`user_id`),
  KEY `idx_created_at` (`created_at`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Background location updates for tracking';

Key Features:

  • Simplified schema with only essential fields
  • Longitude before latitude (geographic convention)
  • User ID reference to workers table
  • Timestamp for tracking history

Sample Data:

INSERT INTO location_updates (user_id, longitude, latitude) VALUES
(1, 106.84560000, -6.20880000),
(1, 106.84561000, -6.20881000);

Security Tables

security_alerts

Security violation logging.

CREATE TABLE `security_alerts` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `alert_type` varchar(100) NOT NULL,
  `alert_data` json DEFAULT NULL,
  `severity` enum('low','medium','high','critical') DEFAULT 'medium',
  `is_resolved` tinyint(1) DEFAULT '0',
  `resolved_at` timestamp NULL DEFAULT NULL,
  `resolved_by` int(11) DEFAULT NULL,
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `resolved_by` (`resolved_by`),
  KEY `idx_user_id` (`user_id`),
  KEY `idx_alert_type` (`alert_type`),
  KEY `idx_severity` (`severity`),
  KEY `idx_is_resolved` (`is_resolved`),
  KEY `idx_created_at` (`created_at`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Security alerts and violations';

Key Features:

  • JSON data field for flexible alert information
  • Severity levels: low, medium, high, critical
  • Resolution tracking with timestamp and resolver
  • Multiple indexes for efficient querying

Sample Data:

INSERT INTO security_alerts (user_id, alert_type, alert_data, severity) VALUES
(1, 'gps_spoofing_detected', '{"apps": ["com.fakegps"], "location": {"lat": -6.2088, "lng": 106.8456}}', 'high'),
(1, 'location_anomaly', '{"distance": 500, "time": 60}', 'medium');

security_checks

Periodic security assessments.

CREATE TABLE `security_checks` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `device_info` json DEFAULT NULL,
  `security_data` json DEFAULT NULL,
  `risk_level` enum('low','medium','high') DEFAULT 'low',
  `risk_score` int(11) DEFAULT '0',
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `idx_user_id` (`user_id`),
  KEY `idx_risk_level` (`risk_level`),
  KEY `idx_timestamp` (`timestamp`),
  KEY `idx_created_at` (`created_at`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Periodic security check results';

Key Features:

  • JSON fields for flexible device and security data
  • Risk level and numeric score
  • Timestamp for tracking check frequency

Sample Data:

INSERT INTO security_checks (user_id, device_info, security_data, risk_level, risk_score) VALUES
(1, '{"platform": "android", "model": "Pixel 6"}', '{"apps": [], "rooted": false}', 'low', 10);

Management Tables

tags

Worker categorization tags.

CREATE TABLE `tags` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `tag_name` varchar(100) NOT NULL,
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `tag_name` (`tag_name`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Tags for categorizing workers';

worker_tags

Many-to-many relationship between workers and tags.

CREATE TABLE `worker_tags` (
  `worker_id` int(11) NOT NULL,
  `tag_id` int(11) NOT NULL,
  PRIMARY KEY (`worker_id`,`tag_id`),
  KEY `idx_worker_id` (`worker_id`),
  KEY `idx_tag_id` (`tag_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Worker tag assignments';

Sample Data:

INSERT INTO tags (tag_name) VALUES ('Production'), ('Day Shift'), ('Night Shift');
INSERT INTO worker_tags (worker_id, tag_id) VALUES (1, 1), (1, 2);

Database Relationships

Entity Relationship Diagram

workers (1) ----< (M) clock_records
workers (1) ----< (M) location_updates  
workers (1) ----< (M) security_alerts
workers (1) ----< (M) security_checks
workers (M) ----< (M) worker_tags >---- (M) tags
qr_codes (1) ----< (M) clock_records

Foreign Key Relationships (Logical)

Note: MyISAM engine doesn't support foreign key constraints, but logical relationships exist:

  • clock_records.worker_idworkers.id
  • clock_records.qr_code_idqr_codes.id
  • location_updates.user_idworkers.id
  • security_alerts.user_idworkers.id
  • security_checks.user_idworkers.id
  • worker_tags.worker_idworkers.id
  • worker_tags.tag_idtags.id

Indexing Strategy

Primary Indexes

  • All tables have auto-increment primary keys
  • Unique constraints on usernames and tag names

Performance Indexes

-- Workers table
KEY `idx_device_uuid` (`device_uuid`)

-- Clock records table  
KEY `idx_worker_id` (`worker_id`)
KEY `idx_event_type` (`event_type`)
KEY `idx_timestamp` (`timestamp`)
KEY `idx_qr_code_id` (`qr_code_id`)

-- Location updates table
KEY `idx_user_id` (`user_id`)
KEY `idx_created_at` (`created_at`)

-- Security tables
KEY `idx_user_id` (`user_id`)
KEY `idx_alert_type` (`alert_type`)
KEY `idx_severity` (`severity`)
KEY `idx_risk_level` (`risk_level`)

Common Queries

Worker Authentication

-- Login verification
SELECT id, role, password_hash 
FROM workers 
WHERE username = ?;

-- Device validation
SELECT id, device_uuid 
FROM workers 
WHERE id = ? AND device_uuid = ?;

Clock Operations

-- Record clock event
INSERT INTO clock_records (worker_id, event_type, qr_code_id, latitude, longitude) 
VALUES (?, ?, ?, ?, ?);

-- Get worker history
SELECT cr.id, cr.event_type, cr.timestamp, COALESCE(qc.name, 'Manual Entry') as qrCodeUsedName 
FROM clock_records cr 
LEFT JOIN qr_codes qc ON cr.qr_code_id = qc.id 
WHERE cr.worker_id = ? 
ORDER BY cr.timestamp DESC;

Location Tracking

-- Insert location update
INSERT INTO location_updates (user_id, longitude, latitude) 
VALUES (?, ?, ?);

-- Get recent locations
SELECT longitude, latitude, created_at 
FROM location_updates 
WHERE user_id = ? AND created_at > DATE_SUB(NOW(), INTERVAL 1 DAY) 
ORDER BY created_at DESC;

Security Monitoring

-- Log security alert
INSERT INTO security_alerts (user_id, alert_type, alert_data, severity) 
VALUES (?, ?, ?, ?);

-- Get security status
SELECT * FROM security_checks 
WHERE user_id = ? 
ORDER BY created_at DESC 
LIMIT 1;

Data Retention Policies

Location Data

  • Keep location_updates for 30 days
  • Archive older data to separate table
  • Clean up automatically via scheduled job

Security Data

  • Keep security_alerts indefinitely for audit
  • Keep security_checks for 90 days
  • Compress old data for storage efficiency

Clock Records

  • Keep all clock_records permanently
  • Essential for payroll and compliance
  • Regular backup and archival

Backup Strategy

Daily Backups

# Full database backup
mysqldump -u username -p database_name > backup_$(date +%Y%m%d).sql

# Table-specific backups
mysqldump -u username -p database_name workers clock_records > critical_$(date +%Y%m%d).sql

Incremental Backups

# Binary log backup for point-in-time recovery
mysqlbinlog --start-datetime="2024-01-01 00:00:00" mysql-bin.000001 > incremental.sql

Performance Optimization

Query Optimization

  • Use EXPLAIN to analyze query performance
  • Add indexes for frequently queried columns
  • Limit result sets with appropriate WHERE clauses

Table Optimization

-- Optimize tables periodically
OPTIMIZE TABLE workers, clock_records, location_updates;

-- Analyze tables for better query planning
ANALYZE TABLE workers, clock_records, location_updates;

Partitioning (Future Enhancement)

-- Partition location_updates by date for better performance
ALTER TABLE location_updates 
PARTITION BY RANGE (TO_DAYS(created_at)) (
    PARTITION p_2024_01 VALUES LESS THAN (TO_DAYS('2024-02-01')),
    PARTITION p_2024_02 VALUES LESS THAN (TO_DAYS('2024-03-01')),
    -- Add more partitions as needed
);

Migration Scripts

Version Updates

-- Example migration script for adding new columns
ALTER TABLE workers ADD COLUMN last_login timestamp NULL DEFAULT NULL;
ALTER TABLE workers ADD KEY idx_last_login (last_login);

-- Update existing data
UPDATE workers SET last_login = created_at WHERE last_login IS NULL;

Data Cleanup

-- Clean old location data (older than 30 days)
DELETE FROM location_updates 
WHERE created_at < DATE_SUB(NOW(), INTERVAL 30 DAY);

-- Archive old security checks
INSERT INTO security_checks_archive 
SELECT * FROM security_checks 
WHERE created_at < DATE_SUB(NOW(), INTERVAL 90 DAY);

DELETE FROM security_checks 
WHERE created_at < DATE_SUB(NOW(), INTERVAL 90 DAY);