# cmoroom.com

Unified Next.js (App Router) app for CMORoom:

- **Public site** — `/`, `/cmoroom`, … (SEO, SSR)
- **Admin dashboard** — `/admin/*` (also via host `dashboard.cmoroom.com` → rewritten to `/admin`)
- **Mock public APIs** — `src/app/api/public/**` (admin: `src/app/api/admin/**`)

## Getting started

```bash
cp .env.example .env.local
npm install
npm run dev
```

| Surface | URL |
| --- | --- |
| Public | http://localhost:3000 |
| Admin | http://localhost:3000/admin (login at `/admin/login`) |

On production/staging, point both public and admin hostnames at the same PM2 process (e.g. `staging.cmoroom.com` + `staging-admin.cmoroom.com`, or `cmoroom.admedia.com` + `cmoroom-admin.admedia.com`). Middleware rewrites the admin host into `/admin/*`.

## Structure

```
src/app/(public)/     # public pages + layout (header/footer)
src/app/admin/        # admin CMS pages + layout
src/app/api/public/   # public mock Route Handlers
src/app/api/admin/    # admin API routes (scaffold)
src/components/       # public UI
src/components/admin/ # admin UI
src/lib/api/          # public API client/services
src/lib/admin/        # admin API client/mocks
src/middleware.ts     # dashboard host → /admin rewrite
```

## Data / API (public)

Pages call `src/lib/api/services/*` → `API_BASE_URL` / `NEXT_PUBLIC_API_BASE_URL` (`/api/public`).

Until MySQL-backed APIs exist for page editors, catalog endpoints read from MySQL via `src/lib/public/content.ts`. Admin content still uses mocks when `NEXT_PUBLIC_USE_MOCK_API=true`.

Admin uses `src/lib/admin/api` with `NEXT_PUBLIC_USE_MOCK_API=true` by default; when off, it calls `NEXT_PUBLIC_ADMIN_API_BASE_URL` (`/api/admin`).

## Auth (admin)

- `POST /api/admin/auth/login` — sets HTTP-only `cmoroom_admin_session` cookie
- `POST /api/admin/auth/logout`
- `GET /api/admin/auth/me`
- Seed: `sql/004_seed_admin.sql` → `admin@cmoroom.com` / `admin123` (dev only)

## Data sources

Server-only modules (never import from client components):

| Module | Path | Env |
| --- | --- | --- |
| Config (Zod) | `src/lib/config.ts` | validates all below |
| MySQL | `src/lib/db.ts` | `DATABASE_URL` |
| Redis | `src/lib/redis.ts` + `src/lib/cache/` | `REDIS_URL` (cache-only; targeted invalidation, never FLUSH) |
| Revalidate | `src/lib/revalidate/` + `POST /api/admin/revalidate` | in-process `afterContentSave`; optional `REVALIDATE_SECRET` |
| Audit | `src/lib/audit.ts` → `cmoroom_audit_logs` | best-effort writes; secrets redacted from metadata |
| S3 | `src/lib/s3.ts` | `AWS_*`, `S3_BUCKET` |
| Envelope | `src/lib/api/envelope.ts` | `{ success, data }` / `{ success: false, error }` |

Health:

- `GET /api/public/health`
- `GET /api/admin/health`

SQL (see `sql/SCHEMA.md` for table → design/API mapping):

```bash
npm run db:setup
# or stepwise:
# npm run db:ensure && npm run db:seed
```

Order: `001_platform` → `002_content` → `003_seed_pages` → `004_seed_admin` → `005_api_entities` → migrations → catalog via `db:seed`.  
`db:ensure` now applies platform + content + seeds + migrations; `db:seed` loads catalog rows.

## Media (S3)

Admin uploads go to S3 when `AWS_*` / `S3_BUCKET` are set (else `public/uploads/` locally).

To move seed/design content URLs (`/assets/img/...`) onto S3:

```bash
# Preview
npm run media:migrate-s3:dry
# Upload + rewrite DB
npm run media:migrate-s3
# After CloudFront: set NEXT_PUBLIC_CDN_URL then
# npx tsx --env-file=.env.local scripts/migrate-assets-to-s3.ts --prefer-cdn
```

Bucket is private; URLs are stored as `/api/media/assets/img/...` and proxied by the app.
(Later: CloudFront + `--prefer-cdn`.) Keys mirror paths: `assets/img/brands/...` and root files like `assets/img/dummy.jpg`.

## Transactional email (invitations)

Approve / Decline in Admin → Invitations sends HTML mail from `emails/invitation-*.html` via **Zoho ZeptoMail**.

```bash
EMAIL_FROM=hello@cmoroom.com
EMAIL_FROM_NAME=CMORoom
EMAIL_REPLY_TO=hello@cmoroom.com
ZEPTOMAIL_API_KEY=   # Agents → SMTP/API → Send Mail Token (raw token or Zoho-enczapikey …)
ZEPTOMAIL_API_URL=https://cpaas.zoho.in/v1.1/email   # India DC; use api.zeptomail.com for US/EU
```


Assets (logo + social icons) are embedded as ZeptoMail `inline_images` (CID), so they do not depend on `https://cmoroom.com/email/*` (apex may still be Wix). CTA / Privacy / Terms links still use Admin → Settings → Site URL (or `NEXT_PUBLIC_SITE_URL`).

If ZeptoMail is unset, the message is logged to the server console (stub).

## PM2

```bash
npm run build
npm run pm2:start
```

## Styles

- Public: `src/styles/global/*` and `src/styles/pages/*`
- Admin: `public/dashboard-assets/css/*` via `PageStyles` + `dashboard.css`
