# Migration, Backup & Seeding Guide

# Migration, Backup & Seeding Guide


# Minimal (default) — ~1 minute
php artisan dev:install --force

# Full test dataset — several minutes
php artisan dev:install --force --test

# Or just the env var, no reinstall needed
SEED_SIZE=test php artisan db:seed --force


# super admin permision update

php artisan roles:sync-super-admin              # sync all tenants
php artisan roles:sync-super-admin --tenant=abc # one tenant only
php artisan roles:sync-super-admin --dry-run    # preview only, no changes
php artisan roles:sync-super-admin -v           # verbose: list missing slugs


#### Demo data clearance from tenant

# Preview what would be cleared
php artisan tenant:clear-demo --tenant=demo --dry-run

# Clear a specific tenant (with confirmation)
php artisan tenant:clear-demo --tenant=demo

# Clear all tenants silently (e.g. in a script)
php artisan tenant:clear-demo --all-tenants --force

# Include product catalogue too
php artisan tenant:clear-demo --tenant=demo --with-products --force


# 2. Re-seed default roles, permissions, settings
php artisan tenant:full-setup demo

> Multi-tenant SaaS — safe data operations for staging, demo, and production environments.


# Apply all new pending migrations to ALL tenants:
docker exec taskco-ecommerce-app php artisan tenants:run migrate \
  --option="path=database/migrations/tenant" --force

# Apply all new pending module migrations to ALL tenants:
docker exec taskco-ecommerce-app php artisan tenants:run "module:migrate" --force

# Push exactly one new migration file to all tenants:
docker exec taskco-ecommerce-app php artisan tenants:run migrate \
  --option="path=database/migrations/tenant/2026_05_01_000001_your_file.php" --force

# Check what's pending on a specific tenant before running:
docker exec taskco-ecommerce-app php artisan tenants:run "migrate:status" --tenants={tenant_id}

---

## Table of Contents

- [Data Protection Rules](#data-protection-rules)
- [Backup](#backup)
- [Migrations](#migrations)
- [Running Only New / Pending Migrations on Tenants](#running-only-new--pending-migrations-on-tenants)
- [Seeders](#seeders)
- [Tenant & Module-Specific Operations](#tenant--module-specific-operations)
- [Safe Demo Server Workflow](#safe-demo-server-workflow)

---

## Data Protection Rules

### NEVER run on staging or production:

```bash
php artisan dev:install          # Drops ALL taskco-* tenant databases
php artisan migrate:fresh        # Wipes the central database
make setup                       # Runs dev:install internally
```

### Always backup before any migration:

```bash
make prod-backup                 # Take a full backup before deploying
```

---

## Backup

### Manual Backup

```bash
# Backup ecommerce stack (default)
make prod-backup

# Backup education stack
make prod-backup-edu

# Direct script usage
bash .docker/postgres/backup.sh              # ecom stack
bash .docker/postgres/backup.sh education    # edu stack

# Custom backup directory
BACKUP_DIR=/custom/path bash .docker/postgres/backup.sh
```

Backups are saved as `{BACKUP_DIR}/{stack}_{TIMESTAMP}.sql.gz`.
Default retention: last **7 days** (override with `KEEP_DAYS=30`).

### Automated Daily Backup (Staging/Production)

Add this cron job on the server to protect demo data:

```bash
crontab -e

# Daily backup at 2am — keeps last 30 days for demo server
0 2 * * * cd /opt/taskco && KEEP_DAYS=30 bash .docker/postgres/backup.sh >> /var/log/taskco-backup.log 2>&1
```

### Restore From Backup

```bash
# List available backups
ls -la ~/taskco-backups/

# Restore a specific backup
gunzip -c ~/taskco-backups/ecom_20260421_020000.sql.gz | \
  docker exec -i taskco-ecommerce-postgres psql -U postgres
```

---

## Migrations

### Architecture

The project uses a **dual-tier migration system**:

| Layer | Location | Database |
|---|---|---|
| Central (platform) | `database/migrations/` | `taskco_saas_app` |
| Tenant (per-tenant) | `database/migrations/tenant/` | `taskco-{slug}` |

### Run Central Migrations

```bash
# Development
make migrate

# Production (requires --force)
make prod-migrate

# Backup first, then migrate (recommended)
make prod-backup && make prod-migrate
```

### Run Tenant Migrations (All Tenants)

```bash
docker exec taskco-ecommerce-app php artisan tenants:run migrate --option="path=database/migrations/tenant" --option="force"
```

### Run Tenant Migrations (Specific Tenant)

```bash
docker exec taskco-ecommerce-app php artisan tenants:run migrate --tenants=demo --option="path=database/migrations/tenant" --option="force"
```

### Run Module Migrations

```bash
# All tenants — all modules
docker exec taskco-ecommerce-app php artisan tenants:run module:migrate --option="force"

# All tenants — specific module
docker exec taskco-ecommerce-app php artisan tenants:run module:migrate --argument="module=ProductApp" --option="force"

# Specific tenant — specific module
docker exec taskco-ecommerce-app php artisan tenants:run module:migrate --argument="module=ProductApp" --tenants=demo --option="force"
```

---

## Running Only New / Pending Migrations on Tenants

### How it works

Laravel records every applied migration in a `migrations` table inside **each database**.
For tenants this means every `taskco-{slug}` database has its own independent `migrations` table.

Running `migrate` (without `fresh`) **automatically skips** any migration already recorded there and **only applies** the ones that are new. This is safe to run repeatedly — it is idempotent.

```
taskco_saas_app          → central migrations table (tracks central DB changes)
taskco-demo              → its own migrations table (tracks what ran on this tenant)
taskco-client-abc        → its own migrations table (independent of demo)
```

So if you add a new migration file today, running `tenants:run migrate` will apply it to
every tenant that does not have it yet, and silently skip tenants that already do.

### Apply all new pending tenant migrations

```bash
# All tenants — applies only what is not yet recorded in each tenant's migrations table
docker exec taskco-ecommerce-app php artisan tenants:run migrate --option="path=database/migrations/tenant" --option="force"

# One specific tenant only
docker exec taskco-ecommerce-app php artisan tenants:run migrate --tenants=demo --option="path=database/migrations/tenant" --option="force"
```

### Apply all new pending module migrations

```bash
# All enabled modules, all tenants
docker exec taskco-ecommerce-app php artisan tenants:run module:migrate --option="force"

# One module, all tenants
docker exec taskco-ecommerce-app php artisan tenants:run module:migrate --argument="module=ProductApp" --option="force"

# One module, one tenant
docker exec taskco-ecommerce-app php artisan tenants:run module:migrate --argument="module=ProductApp" --tenants=demo --option="force"
```

### Apply a single specific new migration file

Use this when you want to push exactly one new migration to all tenants (or one tenant):

```bash
# All tenants — single file
docker exec taskco-ecommerce-app php artisan tenants:run migrate \
  --option="path=database/migrations/tenant/2026_05_01_000001_add_column_to_contacts.php" \
  --option="force"

# One tenant — single file
docker exec taskco-ecommerce-app php artisan tenants:run migrate \
  --option="path=database/migrations/tenant/2026_05_01_000001_add_column_to_contacts.php" \
  --tenants=demo --option="force"
```

> **Note:** Every flag for the inner command must go through `--option=` or `--argument=`.
> Passing `--force`, `--path=`, or `--class=` directly to `tenants:run` does **not** work and causes "too many arguments" errors.

### Check what is pending before running

```bash
# See pending vs applied migrations for one tenant
docker exec taskco-ecommerce-app php artisan tenants:run migrate:status --tenants=demo

# Check central DB
docker exec taskco-ecommerce-app php artisan migrate:status
```

### Full pending-safe deploy sequence (recommended)

```bash
# 1. Backup everything first
make prod-backup

# 2. Central DB — pending only
make prod-migrate

# 3. All tenants — pending tenant migrations only (skips already-applied)
docker exec taskco-ecommerce-app php artisan tenants:run migrate --option="path=database/migrations/tenant" --option="force"

# 4. All tenants — pending module migrations only
docker exec taskco-ecommerce-app php artisan tenants:run module:migrate --option="force"

# 5. Clear caches
make prod-cache
```

---

## Seeders

### Available Seeders

| Seeder | Purpose | Scope |
|---|---|---|
| `AdminDatabaseSeeder` | Admin users and platform setup | Central |
| `RolesAndPermissionsSeeder` | Roles and permission matrix | Tenant |
| `SettingsSeeder` | Platform settings | Tenant |
| `LanguageSeeder` | Supported languages | Tenant |
| `DesignationSeeder` | Job designations | Tenant |
| `BranchSeeder` | Branch/location data | Tenant |
| `EmailTemplateSeeder` | Email templates | Tenant |
| `NotificationTableSeeder` | Notification templates | Tenant |
| `DemoSeeder` | General demo data | Tenant |
| `ContactDemoSeeder` | Demo contacts | Tenant |
| `ThemeSeeder` | Theme categories and options | Central |
| `MediaSeeder` | Demo media assets | Tenant |
| `TenantAdminSeeder` | Tenant admin user setup | Tenant |

### Seed All Tenants

```bash
# Development
make seed

# Production
make prod-seed

# Via artisan
docker exec taskco-ecommerce-app php artisan tenants:seed
```

### Seed a Specific Tenant

```bash
docker exec taskco-ecommerce-app php artisan tenants:run db:seed \
  --tenants={tenant_id}
```

### Seed With a Specific Seeder Class

```bash
# Run one seeder on a specific tenant
docker exec taskco-ecommerce-app php artisan tenants:run db:seed --option="class=ContactDemoSeeder" --tenants=demo

# Run one seeder on all tenants
docker exec taskco-ecommerce-app php artisan tenants:run db:seed --option="class=SettingsSeeder"
```

### Seed a Module

```bash
# All tenants
docker exec taskco-ecommerce-app php artisan tenants:run module:seed --argument="module=ProductApp"

# Specific tenant
docker exec taskco-ecommerce-app php artisan tenants:run module:seed --argument="module=ProductApp" --tenants=demo
```

---

## Tenant & Module-Specific Operations

### Full Tenant Setup (Migrations + Modules + Seeders)

Use this when provisioning a new tenant or rebuilding one from scratch:

```bash
# Complete setup: runs migrations, module migrations, and all seeders
docker exec taskco-ecommerce-app php artisan tenant:full-setup {tenant_id}
```

### Lookup Tenant ID

```bash
docker exec taskco-ecommerce-app php artisan tinker
# Then:
\App\Models\Tenant::all(['id', 'slug', 'company_name'])
```

### Common Per-Tenant Operations

```bash
# Re-apply roles and permissions (safe to re-run)
docker exec taskco-ecommerce-app php artisan tenants:run db:seed --option="class=RolesAndPermissionsSeeder" --tenants=demo

# Refresh email templates
docker exec taskco-ecommerce-app php artisan tenants:run db:seed --option="class=EmailTemplateSeeder" --tenants=demo

# Add demo contacts to a specific tenant
docker exec taskco-ecommerce-app php artisan tenants:run db:seed --option="class=ContactDemoSeeder" --tenants=demo

# Migrate a new module for one tenant only
docker exec taskco-ecommerce-app php artisan tenants:run module:migrate --argument="module=SalesApp" --tenants=demo --option="force"
```

---

## Safe Demo Server Workflow

### Before Any Deployment

```bash
# Step 1: Always backup first
make prod-backup

# Step 2: Run only pending central migrations (never fresh)
make prod-migrate

# Step 3: Run pending tenant migrations
docker exec taskco-ecommerce-app php artisan tenants:run migrate --option="path=database/migrations/tenant" --option="force"

# Step 4: Clear and warm caches
make prod-cache
```

### Quick Reference

| Scenario | Safe Command | Risk |
|---|---|---|
| Deploy new migration | `make prod-backup && make prod-migrate` | Low |
| New module for one tenant | `php artisan tenant:full-setup {id}` | Low |
| Re-seed static config | `tenants:run db:seed --option="class=SettingsSeeder" --tenants=demo` | Low |
| Add demo contacts | `tenants:run db:seed --option="class=ContactDemoSeeder" --tenants=demo` | Low |
| Provision a new tenant | `php artisan tenant:full-setup {id}` | Low |
| `dev:install` on staging | **BLOCKED — wipes all tenant databases** | **DATA LOSS** |
| `migrate:fresh` on staging | **BLOCKED — wipes central database** | **DATA LOSS** |

---

## Makefile Reference

| Command | Description |
|---|---|
| `make migrate` | Run central migrations (dev) |
| `make seed` | Seed database (dev) |
| `make prod-migrate` | Run central migrations with `--force` (prod) |
| `make prod-seed` | Seed production database |
| `make prod-backup` | Backup all ecommerce stack databases |
| `make prod-backup-edu` | Backup education stack databases |
| `make prod-cache` | Clear and warm all Laravel caches |
| `make prod-first-deploy` | Full first deploy: build + start + migrate + seed + passport |
