# Deploying the admin panel to a server (AWS EC2)

How to run the blog admin panel (this Next.js app) on one Ubuntu server, for staging or production. It has no database or file storage: it signs in to the Laravel blog CMS and reads and writes through its API.

For the DevOps/AWS team: [AWS-DEPLOYMENT.md](AWS-DEPLOYMENT.md) is the full handover guide (AWS resources, security group, environment, operations, monitoring, checklist).

```
Editors ─► https://cms-staging.secondlinenumber.com
             │
             ▼
           EC2 (Ubuntu 24.04)
             Nginx :443 ─► Next.js :3200 (PM2, user "cms")
             │
             ▼
           Laravel CMS API (https://secondphonenumber.worksqr.com/api)
             stores articles and media, publishes scheduled articles,
             refreshes the website, serves the public blog API
```

## Files in this folder

| File                     | What it does                                                                                                 |
| ------------------------ | ------------------------------------------------------------------------------------------------------------ |
| `setup-server.sh`        | One-time server setup: Node.js 22, Nginx, certbot, PM2, swap, the `cms` user, folders and an SSH key for git |
| `setup-nginx.sh`         | Installs `nginx/secondline-cms.conf` for your domain and gets the HTTPS certificate                          |
| `deploy.sh`              | Every deploy: pull, build a new release, switch, reload, check; rolls back if it fails                       |
| `ecosystem.config.cjs`   | The PM2 process (`secondline-cms`, Next.js on 127.0.0.1:3200)                                                |
| `staging.env.example`    | The environment file to fill in on the server                                                                |
| `production.env.example` | The same for production (`CMS_ENV="prod"`, `SITE_URL=https://secondlinenumber.com`)                          |

Folders on the server:

```
/srv/secondline-cms/
  repo/        git clone of the repository
  releases/    one folder per deploy (the last three are kept)
  current      → the live release
  shared/.env  the environment, linked into every release
  logs/        cms.out.log, cms.error.log
```

## 1. AWS resources

- **EC2**: Ubuntu 24.04, `t4g.small` or larger (2 GB memory; the script adds 2 GB swap for builds), 20 GB disk. Attach an **Elastic IP**.
- **Security group**: allow 80 and 443 from anywhere; 22 only from your IP, or no SSH at all and use Session Manager. Next.js listens on 127.0.0.1 only, so 3200 stays closed.
- **DNS**: `cms-staging.secondlinenumber.com` → the Elastic IP (A record). On Cloudflare DNS, start with "DNS only" (grey cloud) so certbot can issue the certificate.

## 2. Set up the server (once)

Copy the setup script to the server and run it:

```bash
scp deploy/setup-server.sh ubuntu@<elastic-ip>:
ssh ubuntu@<elastic-ip>
sudo bash setup-server.sh
```

It prints an SSH public key at the end. Add it in Azure DevOps (User settings → SSH public keys), then clone:

```bash
sudo -u cms -H git clone git@ssh.dev.azure.com:v3/BTS-MobileApps/1140_2nd_Phone_Number/1140_2nd_phone_number_admin_panel /srv/secondline-cms/repo
sudo -u cms -H git -C /srv/secondline-cms/repo checkout <branch>
```

## 3. Nginx and HTTPS (once)

DNS for the admin domain must already point at the server.

```bash
sudo bash /srv/secondline-cms/repo/deploy/setup-nginx.sh cms-staging.secondlinenumber.com you@example.com
```

## 4. Environment (once)

```bash
sudo -u cms cp /srv/secondline-cms/repo/deploy/staging.env.example /srv/secondline-cms/shared/.env
sudo -u cms chmod 600 /srv/secondline-cms/shared/.env
sudo -u cms nano /srv/secondline-cms/shared/.env
```

Fill in every `<…>` (`SITE_URL`). `CMS_ENV` picks the Laravel API: `dev` (staging) here; for production copy `production.env.example` instead, which has `CMS_ENV="prod"`.

## 5. First deploy

```bash
sudo -u cms -H bash /srv/secondline-cms/repo/deploy/deploy.sh
```

Sign in at `https://cms-staging.secondlinenumber.com/login` with a blog admin account on the Laravel CMS. HTTPS is required: the session cookie is HTTPS-only, so sign-in fails over plain http.

Content, media, the WordPress import and the website connection (`CMS_URL`, cache refresh) all live on the Laravel CMS now (`docs/laravel-blog-api.md` in the website repository).

## Everyday tasks

| Task                        | Command                                                         |
| --------------------------- | --------------------------------------------------------------- |
| Deploy the latest code      | `sudo -u cms -H bash /srv/secondline-cms/repo/deploy/deploy.sh` |
| Deploy another branch       | `… deploy.sh --branch main`                                     |
| Go back to the last release | `… deploy.sh --rollback`                                        |
| Status, memory              | `sudo -u cms -H pm2 status`                                     |
| Live logs                   | `sudo -u cms -H pm2 logs secondline-cms`                        |
| Restart                     | `sudo -u cms -H pm2 reload secondline-cms`                      |
| Which commit is live        | `cat /srv/secondline-cms/current/REVISION`                      |

After changing `shared/.env`, reload the app (`pm2 reload secondline-cms`). Variables used at build time need a full `deploy.sh`.

## Troubleshooting

| Symptom                                            | Likely cause                                                                                          |
| -------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| Login form does nothing / sends you back           | Not on HTTPS, or the browser blocks the `__Host-` cookie. Use the https URL.                          |
| "Couldn't reach the CMS"                           | `CMS_ENV` (or `LARAVEL_API_URL`) points at the wrong API, or the server can't reach the Laravel host. |
| `Invalid environment variables` in logs            | A value in `shared/.env` isn't a valid URL or number.                                                 |
| `deploy.sh` fails at `npm run build` with "Killed" | Out of memory: check `swapon --show`, or use a larger instance.                                       |
| Uploads fail with 413                              | Larger than Nginx's `client_max_body_size` (12 MB), `MEDIA_MAX_UPLOAD_MB` or Laravel's limit.         |
| A page says "Not available yet"                    | The Laravel CMS doesn't serve that endpoint yet; the notice names it.                                 |

## Production

The same steps, with `cms.secondlinenumber.com` and `production.env.example` (`CMS_ENV="prod"`, i.e. `https://api.secondlinenumber.com/api`, and `SITE_URL=https://secondlinenumber.com`). Optional: proxy `cms.` through Cloudflare (orange cloud) once the certificate is issued, and put Cloudflare Access in front of the whole site.
