# UHS-DTS

Document Tracking System built with Laravel 13, Keycloak, and MinIO.

## Suggested System Requirements

Use these recommended specs for deployment.

| Component | Suggested |
| --- | --- |
| OS | Ubuntu 24.04.4 LTS (Noble Numbat) |
| CPU | 4 vCPU |
| RAM | 8 GB |
| Disk | 50+ GB SSD |
| PHP | 8.4 (latest stable) |
| Composer | 2.8+ (latest stable) |
| Database | PostgreSQL 18.4 (latest stable) |
| Web Server | Nginx 1.28.2 (latest stable) |
| Node.js | v22 LTS (build only) |

## 1) Environment Configuration

Create your environment file first:

```bash
cp .env.example .env
```

On Windows PowerShell (if `cp` is unavailable):

```powershell
Copy-Item .env.example .env
```

### Environment values for Keycloak + MinIO

Set these values in your `.env` (or copy from [ .env.example ](.env.example)):

```dotenv
# App -> Keycloak integration
KEYCLOAK_BASE_URL=http://localhost:8081
KEYCLOAK_REALM=master
KEYCLOAK_VERIFY_SSL=false
KEYCLOAK_ADMIN_USERNAME=admin
KEYCLOAK_ADMIN_PASSWORD=admin

# App -> MinIO (S3-compatible) integration
AWS_ACCESS_KEY_ID=minioadmin
AWS_SECRET_ACCESS_KEY=minioadmin
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=uhs-local
AWS_ENDPOINT=http://localhost:9000
AWS_USE_PATH_STYLE_ENDPOINT=true

# Docker Compose runtime values
DOCKER_RESTART_POLICY=unless-stopped

KEYCLOAK_DB_IMAGE=postgres:16-alpine
KEYCLOAK_IMAGE=keycloak/keycloak:latest
KEYCLOAK_DB_CONTAINER_NAME=uhs_keycloak_db
KEYCLOAK_CONTAINER_NAME=uhs_keycloak
KEYCLOAK_DB_SERVICE_HOST=keycloak-db
KEYCLOAK_DB_PORT=5432
KEYCLOAK_DB_DATABASE=keycloak
KEYCLOAK_DB_USER=keycloak
KEYCLOAK_DB_PASSWORD=keycloak
KEYCLOAK_HOST_PORT=8081
KEYCLOAK_CONTAINER_PORT=8080

MINIO_IMAGE=quay.io/minio/aistor/minio:latest
MINIO_CONTAINER_NAME=uhs_minio
MINIO_ROOT_USER=minioadmin
MINIO_ROOT_PASSWORD=minioadmin
MINIO_API_PORT=9000
MINIO_API_CONTAINER_PORT=9000
MINIO_CONSOLE_PORT=9001
MINIO_CONSOLE_CONTAINER_PORT=9001
```

## 2) Install Docker on Linux (Ubuntu)

```bash
sudo apt update
sudo apt install -y docker.io docker-compose-v2
sudo systemctl enable --now docker
sudo usermod -aG docker $USER
```

Log out and log in again so your user gets Docker group access.

If `docker-compose-v2` is not available on your Ubuntu version:

```bash
sudo apt install -y docker-compose-plugin
```

## 3) Start Keycloak + MinIO with Docker Compose

This project includes [docker-compose.yml](docker-compose.yml) for local Keycloak, Keycloak PostgreSQL, and MinIO/AIStor.

From project root:

```bash
docker compose up -d
```

Check status:

```bash
docker compose ps
```

Stop services:

```bash
docker compose down
```

Service URLs:

- Keycloak: http://localhost:8081
- MinIO API: http://localhost:9000
- MinIO Console: http://localhost:9001

## 4) Laravel Application Setup

Install dependencies:

```bash
composer install
```

Generate app key:

```bash
php artisan key:generate
```

### Database

Current project configuration uses PostgreSQL.

Example PostgreSQL values in `.env`:

- DB_CONNECTION=pgsql
- DB_HOST=your-postgres-host
- DB_PORT=5432
- DB_DATABASE=your_database
- DB_USERNAME=your_username
- DB_PASSWORD=your_password

Run migrations:

```bash
php artisan migrate
```

### Master Data Seeder Workflow

This project includes seeders to:

- Clear request transaction tables (`document_requests`, `document_request_histories`)
- Restore master data snapshots for users, offices, faculties, document types, and workflow templates

Run full reset + seed:

```bash
php artisan migrate:fresh --seed
```

Run seeders without dropping schema:

```bash
php artisan db:seed
```

Clear only request transaction data:

```bash
php artisan db:seed --class=Database\\Seeders\\ClearDocumentRequestDataSeeder
```

Snapshot files are stored in:

- `database/seeders/data/users.json`
- `database/seeders/data/offices.json`
- `database/seeders/data/faculties.json`
- `database/seeders/data/document_types.json`
- `database/seeders/data/workflow_templates.json`

To refresh snapshots from your current database state, run:

```bash
php artisan tinker --execute="file_put_contents(database_path('seeders/data/users.json'), json_encode(DB::table('users')->orderBy('id')->get(), JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE)); file_put_contents(database_path('seeders/data/offices.json'), json_encode(DB::table('offices')->orderBy('id')->get(), JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE)); file_put_contents(database_path('seeders/data/faculties.json'), json_encode(DB::table('faculties')->orderBy('id')->get(), JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE)); file_put_contents(database_path('seeders/data/workflow_templates.json'), json_encode(DB::table('workflow_templates')->orderBy('id')->get(), JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE)); file_put_contents(database_path('seeders/data/document_types.json'), json_encode(DB::table('document_types')->orderBy('id')->get(), JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE));"
```

## 5) Run the Project

Run with Laravel server only:

```bash
php artisan serve
```

Open in browser:

- http://127.0.0.1:8000

## 6) Run Tests

```bash
php artisan test
```

Alternative:

```bash
./vendor/bin/phpunit
```

## 7) API Documentation

- External Student Document Submission API: [docs/external-student-api.md](docs/external-student-api.md)

## Troubleshooting

### Invalid cache path / storage views error

If you see errors like "Please provide a valid cache path", ensure runtime directories exist:

```powershell
New-Item -ItemType Directory -Path storage/framework/views -Force
New-Item -ItemType Directory -Path storage/framework/cache/data -Force
php artisan view:cache
```

## Notes

- App timezone defaults to Asia/Phnom_Penh.
- Keycloak-related environment variables are defined in `.env.example` and must be configured for authentication flows.