185 lines
16 KiB
Bash
185 lines
16 KiB
Bash
#!/bin/bash
|
|||
|
|
# ============================================================================
|
||
|
|
# Arimail(구 mail-gateway) 설치·연동 (v1.5.7, 요구 4·5)
|
||
|
|
#
|
||
|
|
# 웹메이커의 "사이트 메일 시스템" 이 받는 메일·웹메일·IMAP/POP/SMTP 를 쓰려면 메일 서버 Arimail 이 있어야 한다.
|
||
|
|
# 이 스크립트는 Arimail 저장소의 표준 설치 절차(deploy/new-site.sh → setup.sh → deploy.sh)를 이 서버에서 자동으로 밟고,
|
||
|
|
# 끝나면 웹메이커 설정 파일(lib/config.js: mail.*)에 연동 정보를 등록한다. 이미 설치되어 있으면 등록만 한다.
|
||
|
|
#
|
||
|
|
# 사용 (서비스 서버에서 웹메이커 실행 계정으로, 비밀번호 없는 sudo 필요)
|
||
|
|
# 새로 설치 + 등록
|
||
|
|
# deploy/install-arimail.sh --host mail.example.com --mailbox admin@example.com
|
||
|
|
# [--source ~/work/mail-gateway | --bundle <git bundle> | --repo <git URL>] 소스(기본: ~/work/mail-gateway 가 있으면 그것, 없으면 --repo)
|
||
|
|
# [--user arimail] [--app-name arimail] [--port 8790] [--node <node 22.13+ 경로>] [--from-name "이름"] [--title "탭 제목"]
|
||
|
|
# [--limit-accounts N --limit-mailbox 500M --limit-attachment 20M …] (new-site.sh 옵션 그대로 전달)
|
||
|
|
# 이미 설치된(또는 다른 서버의) Arimail 등록만
|
||
|
|
# deploy/install-arimail.sh --register-only --url https://mail.example.com --domain example.com --admin-login webmaker-admin --admin-password '<비밀번호>' [--api-url http://127.0.0.1:8790]
|
||
|
|
# 이 서버에 설치된 Arimail 을 찾아 등록되어 있지 않으면 등록 (install-services.sh --check 가 부른다)
|
||
|
|
# deploy/install-arimail.sh --check
|
||
|
|
#
|
||
|
|
# 하는 일 (설치 모드)
|
||
|
|
# 1. 소스 확보: 로컬 체크아웃을 git bundle 로 묶거나(서버에 저장소 자격 증명을 두지 않음) 저장소를 복제한다.
|
||
|
|
# 2. 실행 계정(없으면 시스템 계정 생성)과 Node 22.13+ 확인(시스템 node 가 맞으면 그것, 아니면 /opt/<app>-node 에 바이너리).
|
||
|
|
# 3. Arimail 의 new-site.sh 로 사이트 설정(standalone, 로그인 ID@도메인 계정) → setup.sh(root: 디렉터리·환경 파일·systemd·sudoers·nginx·certbot 훅) → deploy.sh(실행 계정: 테스트·릴리스·마이그레이션·재시작).
|
||
|
|
# nginx 가 없는 서버에서는 setup.sh 가 nginx 단계에서 멈추므로 같은 템플릿(lib.sh render)으로 nginx 를 뺀 설치를 하고 프록시 설정 예시를 알려 준다.
|
||
|
|
# 4. 환경 파일(gateway.env)의 RESEND_API_KEY 가 비어 있으면 웹메이커 보관소의 메일 발송 키(resend)를 넣는다(같은 Resend 계정으로 보냄).
|
||
|
|
# 5. 웹메이커 전용 Arimail 관리자 계정(webmaker-admin)을 무작위 비밀번호로 만들고 → 웹메이커 설정 파일에 등록:
|
||
|
|
# mail.provider=arimail · mail.url=https://<host> · mail.apiUrl=http://127.0.0.1:<port> · mail.domain · mail.adminLogin · mail.adminPassword(보관소)
|
||
|
|
# 6. 남은 수동 작업(DNS A 레코드, Resend 웹훅 비밀값, 초기 관리자 admin/adminpasswd 비밀번호 변경)을 안내한다.
|
||
|
|
# 다시 실행해도 안전하다(있는 것은 건너뜀).
|
||
|
|
# ============================================================================
|
||
|
|
set -euo pipefail
|
||
|
|
HERE=$(cd "$(dirname "$0")/.." && pwd)
|
||
|
|
CFG() { (cd "$HERE" && node scripts/config.js "$@"); }
|
||
|
|
log() { echo "== $*"; }
|
||
|
|
die() { echo "오류: $*" >&2; exit 1; }
|
||
|
|
|
||
|
|
MODE=install; HOST=""; MAILBOX=""; URL=""; API_URL=""; DOMAIN=""; ADMIN_LOGIN=""; ADMIN_PW=""
|
||
|
|
APP_USER=arimail; APP_NAME=arimail; PORT=8790; NODE_BIN=""; SRC=""; BUNDLE=""; REPO="https://source.ibit.kr/OZ-Group/AriMail.git"; FROM_NAME=""; TITLE=""; SITE=""
|
||
|
|
EXTRA=()
|
||
|
|
while [ $# -gt 0 ]; do
|
||
|
|
case "$1" in
|
||
|
|
--host) HOST=$2; shift 2;; --mailbox) MAILBOX=$2; shift 2;; --url) URL=$2; shift 2;; --api-url) API_URL=$2; shift 2;; --domain) DOMAIN=$2; shift 2;;
|
||
|
|
--admin-login) ADMIN_LOGIN=$2; shift 2;; --admin-password) ADMIN_PW=$2; shift 2;;
|
||
|
|
--user) APP_USER=$2; shift 2;; --app-name) APP_NAME=$2; shift 2;; --port) PORT=$2; shift 2;; --node) NODE_BIN=$2; shift 2;;
|
||
|
|
--source) SRC=$2; shift 2;; --bundle) BUNDLE=$2; shift 2;; --repo) REPO=$2; shift 2;; --from-name) FROM_NAME=$2; shift 2;; --title) TITLE=$2; shift 2;; --site) SITE=$2; shift 2;;
|
||
|
|
--register-only) MODE=register; shift;; --check) MODE=check; shift;;
|
||
|
|
--limit-*|--large-*) EXTRA+=("$1" "$2"); shift 2;;
|
||
|
|
-h|--help) sed -n '2,40p' "$0" | sed 's/^# \{0,1\}//'; exit 0;;
|
||
|
|
*) die "알 수 없는 옵션: $1 (--help)";;
|
||
|
|
esac
|
||
|
|
done
|
||
|
|
|
||
|
|
# ---------- 공통: 웹메이커 설정 파일에 등록 ----------
|
||
|
|
register() { # register <공개 URL> <내부 API URL> <도메인> <관리자 ID> <비밀번호>
|
||
|
|
local url=$1 api=$2 dom=$3 login=$4 pw=$5
|
||
|
|
log "웹메이커 설정 파일에 Arimail 연동 등록 (mail.*)"
|
||
|
|
CFG set mail.provider arimail mail.url "$url" mail.apiUrl "${api:-$url}" mail.domain "$dom" mail.adminLogin "$login" mail.adminPassword "$pw"
|
||
|
|
(cd "$HERE" && node -e "require('./lib/connectors/arimail').status().then((s)=>{console.log(' 연결 확인: '+s.detail); process.exit(s.ok?0:3);})") || echo " (연결 확인 실패 — 주소·계정을 확인하거나 설정 페이지에서 다시 확인하세요)"
|
||
|
|
}
|
||
|
|
# Arimail 설치 안의 user-cli 로 웹메이커 전용 관리자 계정을 만들거나 비밀번호를 재설정한다. 인수: <APP_USER> <ETC_DIR> <NODE_BIN> <APP_DIR> <login> <pw>
|
||
|
|
ensure_admin_account() {
|
||
|
|
local user=$1 etc=$2 node=$3 app=$4 login=$5 pw=$6 envs
|
||
|
|
envs=$(sudo -n -u "$user" grep -E '^(DB_FILE|MAILBOX|LIMIT_ACCOUNTS)=' "$etc/gateway.env" | xargs)
|
||
|
|
if printf '%s' "$pw" | sudo -n -u "$user" env $envs PASSWORD="$pw" "$node" "$app/current/server/user-cli.mjs" add "$login" --admin --name "AI Webmaker" >/dev/null 2>&1; then echo " Arimail 관리자 계정 $login 생성"; else
|
||
|
|
printf '%s' "$pw" | sudo -n -u "$user" env $envs PASSWORD="$pw" "$node" "$app/current/server/user-cli.mjs" passwd "$login" >/dev/null && echo " Arimail 관리자 계정 $login 비밀번호 재설정"; fi
|
||
|
|
}
|
||
|
|
newpw() { openssl rand -base64 24 | tr -d '/+=' | cut -c1-24; }
|
||
|
|
|
||
|
|
# ---------- 등록만 ----------
|
||
|
|
if [ "$MODE" = register ]; then
|
||
|
|
[ -n "$URL" ] && [ -n "$DOMAIN" ] && [ -n "$ADMIN_LOGIN" ] || die "--register-only 에는 --url, --domain, --admin-login 이 필요합니다"
|
||
|
|
if [ -z "$ADMIN_PW" ]; then read -r -s -p "Arimail 관리자($ADMIN_LOGIN) 비밀번호: " ADMIN_PW; echo; fi
|
||
|
|
curl -fsS -m 8 "${API_URL:-$URL}/api/health" >/dev/null || die "Arimail 이 응답하지 않습니다: ${API_URL:-$URL}/api/health"
|
||
|
|
register "$URL" "$API_URL" "$DOMAIN" "$ADMIN_LOGIN" "$ADMIN_PW"; exit 0
|
||
|
|
fi
|
||
|
|
|
||
|
|
# ---------- 설치된 것 찾아 등록 (--check) ----------
|
||
|
|
if [ "$MODE" = check ]; then
|
||
|
|
if [ -n "$(CFG get mail.url)" ]; then echo "Arimail 연동이 이미 등록되어 있습니다: $(CFG get mail.url) (도메인 $(CFG get mail.domain))"; exit 0; fi
|
||
|
|
found=""
|
||
|
|
for f in /srv/*/site.env; do
|
||
|
|
[ -f "$f" ] || continue
|
||
|
|
# shellcheck disable=SC1090
|
||
|
|
eval "$(grep -E '^(APP_NAME|APP_USER|APP_DIR|ETC_DIR|NODE_BIN|PORT|MAIL_HOST|MAILBOX)=' "$f" | sed 's/#.*$//')"
|
||
|
|
[ -n "${APP_NAME:-}" ] && [ -n "${MAIL_HOST:-}" ] && [ -n "${MAILBOX:-}" ] || continue
|
||
|
|
systemctl is-active --quiet "$APP_NAME" 2>/dev/null || continue
|
||
|
|
found=$f; break
|
||
|
|
done
|
||
|
|
[ -n "$found" ] || { echo "이 서버에서 실행 중인 Arimail 을 찾지 못했습니다 (/srv/*/site.env). 새로 설치: $0 --host mail.<도메인> --mailbox admin@<도메인>"; exit 0; }
|
||
|
|
log "설치된 Arimail 발견: $APP_NAME ($MAIL_HOST, $MAILBOX) → 등록"
|
||
|
|
[ -x "$NODE_BIN" ] || NODE_BIN=$(command -v node)
|
||
|
|
PW=$(newpw); ensure_admin_account "$APP_USER" "$ETC_DIR" "$NODE_BIN" "$APP_DIR" webmaker-admin "$PW"
|
||
|
|
register "https://$MAIL_HOST" "http://127.0.0.1:$PORT" "${MAILBOX#*@}" webmaker-admin "$PW"; exit 0
|
||
|
|
fi
|
||
|
|
|
||
|
|
# ---------- 설치 ----------
|
||
|
|
[ -n "$HOST" ] && [ -n "$MAILBOX" ] || die "--host 와 --mailbox 가 필요합니다 (예: --host mail.example.com --mailbox admin@example.com)"
|
||
|
|
[[ "$MAILBOX" == *@*.* ]] || die "--mailbox 는 이메일 주소여야 합니다"
|
||
|
|
DOMAIN=${MAILBOX#*@}; [ -n "$SITE" ] || SITE=$(echo "$APP_NAME" | tr -c 'a-z0-9-\n' '-')
|
||
|
|
sudo -n true 2>/dev/null || die "비밀번호 없는 sudo 가 필요합니다"
|
||
|
|
command -v git >/dev/null || die "git 이 필요합니다 (apt install git)"
|
||
|
|
APP_DIR=/srv/$APP_NAME; ETC_DIR=/etc/$APP_NAME
|
||
|
|
log "Arimail 설치: $HOST · 기본 메일함 $MAILBOX · 서비스 $APP_NAME · 계정 $APP_USER · 포트 $PORT"
|
||
|
|
|
||
|
|
# 1. 소스 → git bundle (실행 계정이 읽을 수 있게 /tmp 에 644)
|
||
|
|
WORK=$(mktemp -d /tmp/arimail-install.XXXXXX); chmod 755 "$WORK"
|
||
|
|
if [ -z "$BUNDLE" ]; then
|
||
|
|
if [ -z "$SRC" ] && [ -d "$HOME/work/mail-gateway/.git" ]; then SRC=$HOME/work/mail-gateway; fi
|
||
|
|
if [ -n "$SRC" ]; then [ -d "$SRC/.git" ] || die "--source 는 git 체크아웃이어야 합니다: $SRC"; log "로컬 소스 $SRC → 번들"; git -C "$SRC" bundle create "$WORK/arimail.bundle" main >/dev/null 2>&1 || git -C "$SRC" bundle create "$WORK/arimail.bundle" HEAD:main >/dev/null;
|
||
|
|
else log "저장소 복제 $REPO"; git clone -q --depth 50 -b main "$REPO" "$WORK/src" || die "저장소를 복제할 수 없습니다 ($REPO). --source 또는 --bundle 로 소스를 주세요"; git -C "$WORK/src" bundle create "$WORK/arimail.bundle" main >/dev/null; fi
|
||
|
|
BUNDLE=$WORK/arimail.bundle
|
||
|
|
fi
|
||
|
|
# 실행 계정(APP_USER)이 읽어야 하므로 작업 폴더에 사본(644)을 둔다 (주어진 번들이 다른 계정 소유여도 되게)
|
||
|
|
if [ "$BUNDLE" != "$WORK/arimail.bundle" ]; then cp "$BUNDLE" "$WORK/arimail.bundle"; BUNDLE=$WORK/arimail.bundle; fi; chmod 644 "$BUNDLE"
|
||
|
|
# new-site.sh/setup.sh 를 실행할 작업 사본 — 복제가 되면 번들도 올바른 것이다 (git bundle verify 는 저장소 안에서만 동작해 쓰지 않는다)
|
||
|
|
git clone -q -b main "$BUNDLE" "$WORK/repo" || die "번들을 열 수 없습니다: $BUNDLE (git bundle create <파일> main 으로 만든 번들이어야 합니다)"
|
||
|
|
VER=$(node -p "require('$WORK/repo/package.json').version" 2>/dev/null || echo '?'); echo " Arimail(mail-gateway) 소스 버전 $VER"
|
||
|
|
|
||
|
|
# 2. Node 22.13+ 와 실행 계정
|
||
|
|
nodeok() { "$1" -e "const [a,b]=process.versions.node.split('.').map(Number); process.exit(a>22||(a===22&&b>=13)?0:1)" 2>/dev/null; }
|
||
|
|
if [ -z "$NODE_BIN" ]; then if command -v node >/dev/null && nodeok "$(command -v node)"; then NODE_BIN=$(command -v node); else NODE_BIN=/opt/$APP_NAME-node/bin/node; fi; fi
|
||
|
|
if ! [ -x "$NODE_BIN" ] || ! nodeok "$NODE_BIN"; then
|
||
|
|
log "Node 22.13+ 가 없어 nodejs.org 바이너리를 /opt/$APP_NAME-node 에 둡니다"
|
||
|
|
NV=v22.23.3; ARCH=$(uname -m); case "$ARCH" in x86_64) NA=x64;; aarch64) NA=arm64;; *) die "지원하지 않는 CPU: $ARCH";; esac
|
||
|
|
curl -fsSL "https://nodejs.org/dist/$NV/node-$NV-linux-$NA.tar.xz" -o "$WORK/node.tar.xz" || die "Node 내려받기 실패 — --node 로 22.13+ 실행 파일을 지정하세요"
|
||
|
|
sudo -n mkdir -p "/opt/$APP_NAME-node" && sudo -n tar -xJf "$WORK/node.tar.xz" -C "/opt/$APP_NAME-node" --strip-components=1; NODE_BIN=/opt/$APP_NAME-node/bin/node
|
||
|
|
fi
|
||
|
|
echo " node: $NODE_BIN ($("$NODE_BIN" --version))"
|
||
|
|
id "$APP_USER" >/dev/null 2>&1 || { sudo -n useradd --system --create-home --home-dir "/home/$APP_USER" --shell /usr/sbin/nologin "$APP_USER"; echo " 실행 계정 $APP_USER 생성"; }
|
||
|
|
|
||
|
|
# 3. 이미 설치되어 있으면 설치 단계는 건너뛴다
|
||
|
|
if [ -d "$APP_DIR/current" ] && systemctl list-unit-files "$APP_NAME.service" 2>/dev/null | grep -q "$APP_NAME.service"; then
|
||
|
|
log "$APP_NAME 이 이미 설치되어 있습니다 ($APP_DIR). 설치는 건너뛰고 등록만 합니다"
|
||
|
|
else
|
||
|
|
log "사이트 설정 파일 (new-site.sh)"
|
||
|
|
bash "$WORK/repo/deploy/new-site.sh" "$SITE" --host "$HOST" --mailbox "$MAILBOX" --user "$APP_USER" --app-name "$APP_NAME" --app-dir "$APP_DIR" --etc-dir "$ETC_DIR" --node "$NODE_BIN" --port "$PORT" ${FROM_NAME:+--from-name "$FROM_NAME"} ${TITLE:+--title "$TITLE"} --login standalone --repo "$REPO" --force "${EXTRA[@]}" | tail -1
|
||
|
|
if command -v nginx >/dev/null 2>&1; then
|
||
|
|
log "서버 준비 (setup.sh — 디렉터리·환경 파일·systemd·sudoers·nginx·certbot 훅)"
|
||
|
|
sudo -n env SITE="$SITE" SOURCE_BUNDLE="$BUNDLE" bash "$WORK/repo/deploy/setup.sh"
|
||
|
|
else
|
||
|
|
# nginx 가 없는 서버(Caddy 등): setup.sh 의 nginx 단계를 뺀 같은 작업을 템플릿으로 수행한다
|
||
|
|
log "nginx 가 없어 웹서버 설정을 뺀 준비를 합니다 (디렉터리·환경 파일·systemd·sudoers)"
|
||
|
|
sudo -n env SITE="$SITE" SOURCE_BUNDLE="$BUNDLE" REPO_WORK="$WORK/repo" bash -c '
|
||
|
|
set -euo pipefail; . "$REPO_WORK/deploy/lib.sh"; load_site "$REPO_WORK/deploy/sites/$SITE.env"
|
||
|
|
mkdir -p "$APP_DIR"/{repo,releases,data} "$ETC_DIR"; chown -R "$APP_USER:$APP_USER" "$APP_DIR"
|
||
|
|
install -o "$APP_USER" -g "$APP_USER" -m 644 "$REPO_WORK/deploy/sites/$SITE.env" "$APP_DIR/site.env"
|
||
|
|
if [ ! -f "$ETC_DIR/gateway.env" ]; then render "$REPO_WORK/deploy/gateway.env.in" "$ETC_DIR/gateway.env"; chown "$APP_USER:$APP_USER" "$ETC_DIR/gateway.env"; chmod 600 "$ETC_DIR/gateway.env"; fi
|
||
|
|
if [ ! -d "$APP_DIR/repo/.git" ]; then sudo -u "$APP_USER" git clone -q -b main "$SOURCE_BUNDLE" "$APP_DIR/repo"; sudo -u "$APP_USER" git -C "$APP_DIR/repo" remote set-url origin "$REPO_URL"; fi
|
||
|
|
render "$APP_DIR/repo/deploy/systemd/mail-gateway.service.in" "/etc/systemd/system/$APP_NAME.service"; chmod 644 "/etc/systemd/system/$APP_NAME.service"
|
||
|
|
sudoers_line > "/etc/sudoers.d/$APP_NAME"; chmod 440 "/etc/sudoers.d/$APP_NAME"; visudo -c -q
|
||
|
|
systemctl daemon-reload; systemctl enable -q "$APP_NAME"'
|
||
|
|
cat <<MSG
|
||
|
|
→ 웹서버에 $HOST 를 127.0.0.1:$PORT 로 프록시하는 설정을 직접 추가하세요. Caddy 예:
|
||
|
|
$HOST { reverse_proxy 127.0.0.1:$PORT }
|
||
|
|
(대용량 첨부 경로 /api/files 는 본문 크기 제한을 없애고, /dl/ 는 버퍼링 없이 — Arimail deploy/nginx/mail.conf.in 참고)
|
||
|
|
MSG
|
||
|
|
fi
|
||
|
|
# 4. 메일 발송 키: 웹메이커 보관소의 Resend 키를 Arimail 환경 파일에 (비어 있을 때만)
|
||
|
|
RESEND_KEY=$(cd "$HERE" && node -e "try{process.stdout.write(require('./lib/vault').secretOf('resend')||'')}catch(e){}")
|
||
|
|
if [ -n "$RESEND_KEY" ] && sudo -n -u "$APP_USER" grep -q '^RESEND_API_KEY=$' "$ETC_DIR/gateway.env"; then
|
||
|
|
printf '%s' "$RESEND_KEY" | sudo -n -u "$APP_USER" env ENVF="$ETC_DIR/gateway.env" bash -c 'k=$(cat); sed -i "s|^RESEND_API_KEY=.*|RESEND_API_KEY=$k|" "$ENVF"' && echo " gateway.env: RESEND_API_KEY ← 웹메이커 메일 발송 키"
|
||
|
|
fi
|
||
|
|
log "배포 (deploy.sh — 테스트·릴리스·DB 마이그레이션·서비스 시작)"
|
||
|
|
sudo -n -u "$APP_USER" env SOURCE_BUNDLE="$BUNDLE" "$APP_DIR/repo/deploy/deploy.sh"
|
||
|
|
fi
|
||
|
|
log "상태 확인"; for i in $(seq 1 30); do curl -fsS -m 3 "http://127.0.0.1:$PORT/api/health" >/dev/null 2>&1 && break; sleep 1; [ "$i" = 30 ] && die "Arimail 이 응답하지 않습니다: journalctl -u $APP_NAME -n 50"; done
|
||
|
|
echo " $(curl -fsS -m 3 "http://127.0.0.1:$PORT/api/health")"
|
||
|
|
|
||
|
|
# 5. 웹메이커 전용 관리자 계정 → 설정 파일 등록
|
||
|
|
PW=$(newpw); ensure_admin_account "$APP_USER" "$ETC_DIR" "$NODE_BIN" "$APP_DIR" webmaker-admin "$PW"
|
||
|
|
register "https://$HOST" "http://127.0.0.1:$PORT" "$DOMAIN" webmaker-admin "$PW"
|
||
|
|
rm -rf "$WORK"
|
||
|
|
|
||
|
|
# 6. 남은 수동 작업
|
||
|
|
cat <<MSG
|
||
|
|
== 완료. Arimail $VER — 웹메일 https://$HOST/ 관리자 페이지 https://$HOST/admin (서비스 $APP_NAME, 내부 http://127.0.0.1:$PORT)
|
||
|
|
남은 작업:
|
||
|
|
1) DNS: $HOST A 레코드 → 이 서버 공인 IP (프록시 없이). 바뀌면 $APP_NAME-tls.timer 가 인증서를 받아 443·메일 포트 TLS 를 켭니다.
|
||
|
|
(바로: sudo bash $APP_DIR/repo/deploy/tls-when-ready.sh $APP_DIR/site.env)
|
||
|
|
2) Resend: 도메인 $DOMAIN 을 수신·발신 도메인으로 등록하고 Webhooks → https://$HOST/api/webhooks/resend (email.received) 의 Signing Secret 을
|
||
|
|
$ETC_DIR/gateway.env 의 RESEND_WEBHOOK_SECRET 에 넣은 뒤 sudo systemctl restart $APP_NAME
|
||
|
|
3) 초기 관리자 admin / adminpasswd 로 https://$HOST/admin 에 들어가 비밀번호를 바꾸세요 (웹메이커는 별도 계정 webmaker-admin 을 씁니다).
|
||
|
|
4) 웹메이커 관리자 콘솔 › 메일 시스템에서 메일 도메인이 $DOMAIN 인 사이트의 메일 시스템을 켜면 사이트 관리자 계정의 메일함이 Arimail 에 만들어집니다.
|
||
|
|
MSG
|