# CYDM — Development Environment

> **The development environment is [Laradock](https://laradock.io).** Everything runs in Docker containers.
> Nothing (PHP, Composer, Node, MySQL, Redis) is installed on Windows directly — all commands run *inside* the containers.

---

## 1. Layout

Laradock sits **beside** the project, not inside it. Laradock's `.env` sets `APP_CODE_PATH_HOST=../`, which mounts the whole `DEVELOPMENT ENV` folder at `/var/www` inside the containers.

```
C:\Users\dismas\Documents\DEVELOPMENT ENV\
├── laradock\                 <- the Docker environment (shared by all projects)
│   ├── docker-compose.yml
│   ├── .env                  <- ports, versions, which PHP extensions get built
│   ├── nginx\sites\
│   │   └── cydm.conf         <- our vhost
│   └── mysql\docker-entrypoint-initdb.d\
│       └── cydm.sql          <- our database + user
│
└── cydm\                     <- THIS PROJECT  ->  /var/www/cydm in the container
```

So the mapping is:

| Windows | Inside container |
|---|---|
| `C:\Users\dismas\Documents\DEVELOPMENT ENV\` | `/var/www/` |
| `C:\Users\dismas\Documents\DEVELOPMENT ENV\cydm\` | `/var/www/cydm/` |

Edit files in Windows with your editor; the containers see the changes immediately.

---

## 2. Getting a shell — the daily entry point

```powershell
cd "C:\Users\dismas\Documents\DEVELOPMENT ENV\laradock"
docker compose exec workspace bash
```

Then, inside the container:

```bash
cd /var/www/cydm
```

You are now in the project with PHP 8.3, Composer, Node and npm all available. This is where every `php artisan`, `composer` and `npm` command is run.

```
PS C:\Users\dismas\Documents\DEVELOPMENT ENV\laradock> docker compose exec workspace bash
root@201a38455b26:/var/www# cd cydm/
root@201a38455b26:/var/www/cydm# php artisan about
```

**One-liner** (run a single command without an interactive shell):

```bash
docker compose exec workspace bash -lc "cd /var/www/cydm && php artisan migrate"
```

---

## 3. Starting and stopping

```powershell
cd "C:\Users\dismas\Documents\DEVELOPMENT ENV\laradock"

# Start what CYDM needs
docker compose up -d nginx mysql redis workspace

# Status
docker compose ps

# Stop everything (data survives — it lives in ~/.laradock/data)
docker compose stop

# Logs
docker compose logs -f nginx
```

> Other projects share this Laradock instance. `docker compose down` affects **all** of them — prefer `stop`.

---

## 4. Services

| Service | Hostname *inside* Docker | Port from Windows | Notes |
|---|---|---|---|
| nginx | `nginx` | 80, 81, 443 | Serves the app |
| php-fpm | `php-upstream` | — | PHP 8.3.33 |
| mysql | `mysql` | 3306 | MySQL 8.4 |
| redis | `redis` | 6379 | Cache, sessions, queues |
| workspace | `workspace` | 5173 (Vite), 3000, 8080 | Where you run commands |

**The distinction that trips everyone up:** inside the container the database host is `mysql`, not `127.0.0.1`. From a Windows GUI tool (TablePlus, DBeaver) it is `127.0.0.1:3306`.

### Credentials

| | Value |
|---|---|
| Database | `cydm` |
| Test database | `cydm_testing` |
| User | `cydm` |
| Password | `cydm_secret` |
| Root password | `root` |

These are **local development credentials only** and are checked into the repo deliberately so the environment is reproducible. Production credentials never enter git.

---

## 5. URLs

| URL | Purpose | Needs hosts entry? |
|---|---|---|
| `http://localhost:81` | **Fallback** — works immediately | No |
| `http://cydm.test` | Central / marketing / signup | Yes |
| `http://admin.cydm.test` | Platform super-admin | Yes |
| `http://demo.cydm.test` | Demo tenant | Yes |

### Why there are two ways in

`nginx/sites/cydm.conf` defines two server blocks:

1. **Port 80**, `server_name cydm.test *.cydm.test` — the real thing. Wildcard subdomains let the tenancy middleware resolve a tenant from the Host header, exactly as production will.
2. **Port 81**, `default_server` — matches any hostname, so `http://localhost:81` reaches the app with no hosts-file entry at all.

The fallback exists because editing the Windows hosts file requires Administrator rights. Use it for quick checks; use `cydm.test` when testing anything tenant-related, since that is the path that exercises real subdomain routing.

### Adding the hostnames

nginx handles `*.cydm.test` fine, but **the Windows hosts file does not support wildcards** — each subdomain needs its own line.

Open PowerShell **as Administrator** and run:

```powershell
cd "C:\Users\dismas\Documents\DEVELOPMENT ENV\cydm"
.\scripts\setup-hosts.ps1
```

That adds `cydm.test`, `admin.cydm.test`, and the `demo` / `akiba` / `umoja` tenant subdomains, then flushes the DNS cache. It is idempotent — re-running replaces the block rather than duplicating it, and it backs the file up first.

To add another tenant later:

```powershell
.\scripts\setup-hosts.ps1 -Tenants demo,akiba,umoja,newtenant
```

To undo everything: `.\scripts\setup-hosts.ps1 -Remove`

---

## 6. Adding a database (the Laradock way)

Laradock auto-runs `.sql` files in `mysql/docker-entrypoint-initdb.d/` — **but only when the MySQL data directory is created for the first time**. Since `~/.laradock/data/mysql` already exists, a new script must be run manually.

Our script lives at `laradock/mysql/docker-entrypoint-initdb.d/cydm.sql`. To apply it:

```bash
docker compose exec mysql bash -c "mysql -u root -proot < /docker-entrypoint-initdb.d/cydm.sql"
```

Keeping the script in place means a teammate (or a rebuilt data directory) gets the database automatically.

Verify:

```bash
docker compose exec mysql mysql -u cydm -pcydm_secret -e "SHOW DATABASES;"
```

---

## 7. Adding a website (the Laradock way)

1. Create `laradock/nginx/sites/{project}.conf`, copying an existing one as a template.
2. Set `server_name` and point `root` at `/var/www/{project}/public`.
3. Validate and reload — no rebuild needed:

```bash
docker compose exec nginx nginx -t          # validate first, always
docker compose exec nginx nginx -s reload
```

4. Add the hostname to the Windows hosts file.

`fastcgi_pass php-upstream;` is the Laradock convention — it refers to the php-fpm container and should be copied verbatim.

---

## 8. Frontend / Vite

Vite runs **inside** the workspace container, and Laradock publishes port 5173:

```bash
# inside workspace, at /var/www/cydm
npm run dev
```

`vite.config.ts` must bind to `0.0.0.0`, otherwise the dev server is unreachable from Windows:

```ts
server: {
    host: '0.0.0.0',
    port: 5173,
    hmr: { host: 'localhost' },
}
```

`host: '0.0.0.0'` listens on all container interfaces; `hmr.host: 'localhost'` tells the *browser* where to open its websocket. Both are required.

For a production-style check, build instead: `npm run build`.

---

## 9. Common commands

```bash
# All run inside workspace, from /var/www/cydm

php artisan about                  # environment sanity check
php artisan migrate                # run migrations
php artisan migrate:fresh --seed   # rebuild the database
php artisan test                   # full test suite
php artisan tinker                 # REPL

composer install
npm install && npm run dev

php artisan queue:work             # or: php artisan horizon
php artisan optimize:clear         # clear every cache
```

---

## 10. Troubleshooting

| Symptom | Cause | Fix |
|---|---|---|
| 404 on every URL | `public/` missing or wrong `root` | Check `root` in `cydm.conf` matches `/var/www/cydm/public` |
| 502 Bad Gateway | php-fpm down | `docker compose restart php-fpm` |
| `SQLSTATE[HY000] [2002]` | Using `127.0.0.1` instead of `mysql` | Set `DB_HOST=mysql` in `.env` |
| Vite assets 404 | Dev server not bound to `0.0.0.0` | See §8 |
| HMR never connects | `hmr.host` wrong | Set `hmr: { host: 'localhost' }` |
| `cydm.test` doesn't resolve | Hosts entry missing | Run `scripts\setup-hosts.ps1` as Administrator, or use `http://localhost:81` |
| Permission errors on `storage/` | UID mismatch | `chown -R laradock:laradock storage bootstrap/cache` inside workspace |
| nginx won't reload | Config error | `docker compose exec nginx nginx -t` shows the line |

---

## 11. Known environment constraints

Recorded here because they shaped real decisions — see [DEVELOPMENT_PLAN.md](DEVELOPMENT_PLAN.md) §2.1.

- **No PostgreSQL driver.** `PHP_FPM_INSTALL_PGSQL`, `PHP_WORKER_INSTALL_PGSQL` and `WORKSPACE_INSTALL_PG_CLIENT` are all `false` in Laradock's `.env`, and the `postgres` container is not running. Using Postgres would mean flipping those flags and rebuilding three images. We use MySQL 8.4 instead.
- **`WORKSPACE_INSTALL_PNPM=false`.** Use `npm`, or the `pnpm dlx` equivalent `npx`.
- **Shared environment.** Other projects (paisha, tanpesa, eventyetu, africraft…) use this Laradock instance. Avoid `docker compose down` and avoid changing shared `.env` values without checking what else depends on them.
- **PHP 8.3.33.** Laravel 13 supports 8.3–8.5, so this is the floor of the supported range. Fine, but worth knowing when reading docs that assume 8.4 syntax.
