# 오피셜메일

혼자 시작하는 사업자를 위한 무료 기업메일 서비스 MVP 레포입니다.

## 포함한 범위

- Next.js 랜딩페이지
- 로그인 페이지
- 대시보드
- 헤더 우측 상단 프로필 아이콘
- 프로필 아이콘으로 진입하는 메일 사이트
- 설정 미완료 사용자를 위한 로딩 후 페이드 오버레이
- 메일 설정 페이지
- Ubuntu 서버 배포 스크립트
- DNS / Mailcow 문서

## 라우트

- `/` 랜딩페이지
- `/login` 로그인
- `/dashboard` 사용자 대시보드
- `/mail` 메일 사이트 진입 페이지
- `/mail-setup` 메일 설정 페이지

## 로컬 실행

```bash
npm install
npm run dev
```

`.env.production`:

```bash
NEXT_PUBLIC_APP_URL=https://your-domain.example
NEXT_PUBLIC_MAIL_APP_URL=https://mail.your-domain.example
DB_HOST=your-db-host
DB_PORT=3306
DB_USER=your-db-user
DB_PASSWORD=your-db-password
DB_NAME=official_mail
MAILCOW_BASE_URL=https://mx1.your-domain.example
MAILCOW_API_KEY=your-mailcow-rw-api-key
MAILCOW_ALLOW_SELF_SIGNED=false
MAILCOW_REQUEST_TIMEOUT_MS=15000
MAILCOW_DOMAIN_ALIAS_LIMIT=400
MAILCOW_DOMAIN_MAILBOX_LIMIT=10
MAILCOW_DOMAIN_QUOTA_MB=10240
MAILCOW_MAILBOX_QUOTA_MB=3072
MAILCOW_DKIM_KEY_SIZE=2048
MX_HOSTNAME=mx1.your-domain.example
DKIM_SELECTOR=om1
DMARC_RUA=mailto:dmarc@your-domain.example
```

`MAILCOW_API_KEY` 는 Mailcow 관리자 화면의 `Configuration -> Access -> API` 에서
읽기/쓰기 키로 생성하고, 호출 서버 IP 를 허용 목록에 추가해야 합니다.

`MAILCOW_BASE_URL` 은 Swagger 주소인 `/api` 가 아니라 Mailcow 호스트 루트
예: `https://mx1.your-domain.example` 로 넣어야 합니다. 앱이 내부에서 `/api/v1/...`
경로를 직접 붙여 호출합니다.

공개 도메인 + 정상 인증서 구조라면 `MAILCOW_ALLOW_SELF_SIGNED=false` 가 맞습니다.
호스트 로컬 `127.0.0.1:8443` 또는 self-signed 인증서를 쓰는 구조에서만
`MAILCOW_ALLOW_SELF_SIGNED=true` 를 사용하세요.

현재 오피셜메일 앱은 Mailcow API 를 브라우저에서 직접 호출하지 않고 Next.js 서버가
서버-투-서버로 호출합니다. 따라서 기본 구성에서는 Mailcow CORS 설정이 필요하지 않습니다.
Mailcow 쪽에는 API 키와 허용 IP 만 맞추면 됩니다.

만약 별도의 프론트엔드에서 브라우저가 Mailcow API 를 직접 호출하도록 만들 경우에만
CORS 를 설정하세요.

- `Access-Control-Allow-Origin`: 호출하는 웹앱의 정확한 Origin
  예: `https://mail.officialsite.kr`
- `Access-Control-Allow-Methods`: `GET, POST, PUT, DELETE, OPTIONS`

`mx1.officialsite.kr` 는 API 서버 주소이지 브라우저 Origin 값이 아니므로,
CORS Origin 칸에는 보통 넣으면 안 됩니다.

도메인 등록과 Mailcow 프로비저닝은 같은 Next.js repo 안에서 처리하므로 별도 repo 는 필요하지 않습니다.

## DB 스키마 생성

```bash
python scripts/setup_official_mail_db.py
```

이 스크립트는 MySQL/MariaDB 서버에 `official_mail` 데이터베이스와 관련 테이블을 생성합니다.

## 서버 세팅 문서

- [deploy/ubuntu/MAIL_SERVICE_RUNBOOK.md](/D:/works/kscompany/official-mail/deploy/ubuntu/MAIL_SERVICE_RUNBOOK.md)
- [deploy/ubuntu/04-host-nginx-mail.conf.example](/D:/works/kscompany/official-mail/deploy/ubuntu/04-host-nginx-mail.conf.example)
- [.github/workflows/deploy-officialsite.yml](/D:/works/kscompany/official-mail/.github/workflows/deploy-officialsite.yml)

## 주의

실서비스용 비밀값은 절대로 레포나 GitHub Actions 워크플로에 넣지 말고,
서버의 `.env.production` 또는 별도 비밀 저장소에서만 관리해야 합니다.

## 메일 동기화 로그 확인

메일함 동기화가 멈춘 것 같으면 아래 순서대로 확인하면 됩니다.

앱 로그 확인

```bash
pm2 logs officialsite-kr --lines 200
```

AI 릴레이 답장 worker 실행

```bash
pm2 start npm --name official-mail-ai-reply-relay -- run worker:ai-reply-relay
pm2 save
pm2 logs official-mail-ai-reply-relay --lines 100
```

`OFFICIAL_MAIL_CRON_SECRET` 은 Next 앱과 worker 프로세스에서 같은 값을 사용해야 합니다.

앱 로그를 파일 tail 로 계속 보기

```bash
tail -f ~/.pm2/logs/officialsite-kr-out.log ~/.pm2/logs/officialsite-kr-error.log
```

앱 로그에서 메일 관련 에러만 빠르게 보기

```bash
grep -Ei "mailbox|imap|smtp|sync|mailcow|error" ~/.pm2/logs/officialsite-kr-*.log | tail -100
```

Mailcow 전체 메일 관련 로그 실시간 보기

```bash
cd /opt/mailcow-dockerized
docker compose logs -f --tail=100 dovecot-mailcow postfix-mailcow nginx-mailcow php-fpm-mailcow
```

IMAP 수신 동기화 로그만 보기

```bash
cd /opt/mailcow-dockerized
docker compose logs -f --tail=100 dovecot-mailcow
```

SMTP 수신/발송 로그만 보기

```bash
cd /opt/mailcow-dockerized
docker compose logs -f --tail=100 postfix-mailcow
```

DB 조회 전에 앱 환경변수 로드

```bash
cd /var/www/front/official-mail
set -a
source .env.production
set +a
```

현재 동기화 상태 DB 확인

```bash
mysql -h "$DB_HOST" -P "$DB_PORT" -u "$DB_USER" -p"$DB_PASSWORD" "$DB_NAME" -e "
SELECT
  id,
  email,
  last_sync_at,
  LEFT(COALESCE(last_sync_error, ''), 300) AS last_sync_error
FROM mailboxes
ORDER BY id DESC;
"
```

특정 메일함의 폴더별 동기화 상태 확인

```bash
MAILBOX_EMAIL="대표메일주소@example.com"
mysql -h "$DB_HOST" -P "$DB_PORT" -u "$DB_USER" -p"$DB_PASSWORD" "$DB_NAME" -e "
SELECT
  f.system_name,
  f.name,
  f.remote_total,
  f.remote_unseen,
  f.last_synced_at
FROM mailbox_folders f
JOIN mailboxes m ON m.id = f.mailbox_id
WHERE m.email = '$MAILBOX_EMAIL'
ORDER BY f.sort_order ASC, f.id ASC;
"
```

최근 수신 메일이 DB 에 들어왔는지 확인

```bash
MAILBOX_EMAIL="대표메일주소@example.com"
mysql -h "$DB_HOST" -P "$DB_PORT" -u "$DB_USER" -p"$DB_PASSWORD" "$DB_NAME" -e "
SELECT
  mm.id,
  f.system_name,
  mm.from_address,
  mm.subject,
  mm.is_read,
  mm.received_at
FROM mailbox_messages mm
JOIN mailbox_folders f ON f.id = mm.folder_id
JOIN mailboxes m ON m.id = mm.mailbox_id
WHERE m.email = '$MAILBOX_EMAIL'
ORDER BY mm.received_at DESC, mm.id DESC
LIMIT 30;
"
```

주로 보는 포인트

- `mailboxes.last_sync_error` 에 `mailbox-auth-missing`, `mailbox-auth-invalid`, `mailbox-imap-failed:*` 가 남는지 확인
- `mailbox_folders.last_synced_at` 이 오래 멈춰 있으면 IMAP 동기화가 안 돈 상태인지 확인
- `dovecot-mailcow` 로그에 로그인 실패나 폴더 open 실패가 있는지 확인
- `postfix-mailcow` 로그에 실제 수신은 됐는데 앱 쪽만 반영이 안 되는지 확인
