create-entity-server 0.0.25 → 0.0.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/template/.env.example +38 -0
- package/template/configs/auth/cors.json +15 -0
- package/template/configs/auth/identity.json +27 -0
- package/template/configs/auth/jwt.json +12 -0
- package/template/configs/{oauth.json → auth/oauth.json} +7 -4
- package/template/configs/auth/password.json +33 -0
- package/template/configs/auth/privacy_policy.json +28 -0
- package/template/configs/{security.json → auth/security.json} +4 -2
- package/template/configs/auth/two_factor.json +12 -0
- package/template/configs/database.json +0 -159
- package/template/configs/database.json.example +186 -0
- package/template/configs/extensions/backup.json +46 -0
- package/template/configs/extensions/pg.json +37 -0
- package/template/configs/extensions/storage.json +148 -0
- package/template/configs/extensions/tax-invoice.json +59 -0
- package/template/configs/keys/.gitkeep +0 -0
- package/template/configs/keys/apns.p8.example +7 -0
- package/template/configs/keys/firebase.pem.example +7 -0
- package/template/configs/notification/alimtalk.json +75 -0
- package/template/configs/{push.json → notification/push.json} +1 -2
- package/template/configs/notification/sms.json +54 -0
- package/template/configs/notification/smtp.json +43 -0
- package/template/configs/server.json +2 -0
- package/template/entities/README.md +20 -23
- package/template/entities/System/Auth/account.json +17 -1
- package/template/entities/System/Auth/account_audit.json +23 -0
- package/template/entities/System/Auth/account_device.json +63 -0
- package/template/entities/System/Auth/account_login_log.json +54 -0
- package/template/entities/System/Auth/account_oauth.json +45 -0
- package/template/entities/System/Auth/api_keys.json +1 -1
- package/template/entities/System/Auth/identity_verification.json +95 -0
- package/template/entities/System/Auth/license.json +1 -1
- package/template/entities/System/Auth/password_history.json +20 -0
- package/template/entities/System/Auth/rbac_roles.json +1 -1
- package/template/entities/System/Backup/backup_log.json +62 -0
- package/template/entities/System/Email/smtp_log.json +83 -0
- package/template/entities/System/Email/smtp_msg.json +104 -0
- package/template/entities/System/Notification/alimtalk_log.json +45 -0
- package/template/entities/System/Notification/alimtalk_msg.json +39 -0
- package/template/entities/System/Notification/friendtalk_log.json +48 -0
- package/template/entities/System/Notification/friendtalk_msg.json +63 -0
- package/template/entities/System/Notification/sms_log.json +26 -0
- package/template/entities/System/Notification/sms_msg.json +52 -0
- package/template/entities/System/Notification/sms_verification.json +40 -0
- package/template/entities/System/Payment/pg_cancel.json +45 -0
- package/template/entities/System/Payment/pg_order.json +88 -0
- package/template/entities/System/Payment/pg_webhook_log.json +37 -0
- package/template/entities/System/Push/push_log.json +71 -0
- package/template/entities/System/Push/push_msg.json +55 -0
- package/template/entities/System/Storage/file_backup_log.json +46 -0
- package/template/entities/System/Storage/file_download_log.json +41 -0
- package/template/entities/System/Storage/file_meta.json +59 -0
- package/template/entities/System/system_audit_log.json +2 -1
- package/template/entities/company.json +1 -1
- package/template/entities/{product.json → goods.json} +6 -10
- package/template/entities/todo.json +1 -1
- package/template/samples/entities/README.md +2 -2
- package/template/scripts/generate-env-keys.sh +16 -0
- package/template/templates/email/account/dormancy_warning.html +20 -0
- package/template/templates/email/account/password_expiry_warning.html +21 -0
- package/template/templates/email/auth/2fa_disabled.html +23 -0
- package/template/templates/email/auth/2fa_recovery_regenerated.html +31 -0
- package/template/templates/email/auth/2fa_setup_complete.html +43 -0
- package/template/templates/email/auth/email_verification.html +18 -0
- package/template/templates/email/auth/force_reset.html +18 -0
- package/template/templates/email/auth/password_reset.html +19 -0
- package/template/templates/email/auth/verification.html +15 -0
- package/template/templates/email/auth/verification_link.html +25 -0
- package/template/templates/email/auth/welcome.html +18 -0
- package/template/templates/email/backup/backup_completed.html +35 -0
- package/template/templates/email/backup/backup_failed.html +27 -0
- package/template/templates/email/backup/backup_partial.html +31 -0
- package/template/templates/email/layout.html +47 -0
- package/template/templates/email/order/order_confirmation.html +30 -0
- package/template/templates/email/storage/storage_quota_exceeded.html +37 -0
- package/template/templates/email/storage/storage_quota_warning.html +37 -0
- package/template/configs/cors.json +0 -7
- package/template/configs/jwt.json +0 -8
- package/template/entities/Account/account_audit.json +0 -16
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "file_meta",
|
|
3
|
+
"description": "파일 메타데이터. 업로드된 파일의 저장 위치·크기·해시·상태를 관리합니다. 파일 핸들러가 자동 기록하며 API를 통한 직접 수정은 허용되지 않습니다. 사용자 환경에 맞게 index·fields를 자유롭게 확장할 수 있습니다.",
|
|
4
|
+
"history": false,
|
|
5
|
+
"hard_delete": true,
|
|
6
|
+
"read_only": true,
|
|
7
|
+
"index": {
|
|
8
|
+
"backup_status": {
|
|
9
|
+
"comment": "백업 동기화 상태",
|
|
10
|
+
"type": ["none", "pending", "synced", "failed", "skipped"],
|
|
11
|
+
"default": "none"
|
|
12
|
+
},
|
|
13
|
+
"size": {
|
|
14
|
+
"comment": "파일 크기 (bytes)",
|
|
15
|
+
"type": "bigint unsigned"
|
|
16
|
+
},
|
|
17
|
+
"status": {
|
|
18
|
+
"comment": "파일 상태",
|
|
19
|
+
"type": ["active", "pending", "orphan", "deleted"],
|
|
20
|
+
"default": "pending",
|
|
21
|
+
"required": true
|
|
22
|
+
},
|
|
23
|
+
"uuid": {
|
|
24
|
+
"comment": "파일 고유 식별자 (UUID v4). 저장 경로·다운로드 URL의 키로 사용",
|
|
25
|
+
"type": "varchar(36)",
|
|
26
|
+
"required": true,
|
|
27
|
+
"unique": true
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"fields": {
|
|
31
|
+
"backup_retries": {
|
|
32
|
+
"comment": "백업 재시도 횟수"
|
|
33
|
+
},
|
|
34
|
+
"content_hash": {
|
|
35
|
+
"comment": "파일 SHA-256 해시 (중복 탐지·무결성 검증용)"
|
|
36
|
+
},
|
|
37
|
+
"entity_name": {
|
|
38
|
+
"comment": "파일이 첨부된 엔티티 이름"
|
|
39
|
+
},
|
|
40
|
+
"entity_seq": {
|
|
41
|
+
"comment": "파일이 첨부된 엔티티 레코드 seq. 업로드 직후 엔티티 미연결 시 NULL"
|
|
42
|
+
},
|
|
43
|
+
"field_name": {
|
|
44
|
+
"comment": "파일이 첨부된 필드 이름 (file/file[] 타입 필드)"
|
|
45
|
+
},
|
|
46
|
+
"mime_type": {
|
|
47
|
+
"comment": "파일 MIME 타입 (image/jpeg, application/pdf 등)"
|
|
48
|
+
},
|
|
49
|
+
"original_name": {
|
|
50
|
+
"comment": "업로드 시 원본 파일명 (보안 대상 — 암호화 저장)"
|
|
51
|
+
},
|
|
52
|
+
"storage_key": {
|
|
53
|
+
"comment": "스토리지 백엔드 내 상대 경로 (예: 2026/02/27/contact/uuid.jpg)"
|
|
54
|
+
},
|
|
55
|
+
"storage_path": {
|
|
56
|
+
"comment": "라이선스 접두어 포함 전체 경로 (예: 3/2026/02/27/contact/uuid.jpg)"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "system_audit_log",
|
|
3
|
-
"description": "시스템 감사 로그. 서버 레벨에서 자동 기록되며 API를 통한 직접 수정은 허용되지 않습니다. JWT 인증 시에만 account_seq
|
|
3
|
+
"description": "시스템 감사 로그. 서버 레벨에서 자동 기록되며 API를 통한 직접 수정은 허용되지 않습니다. JWT 인증 시에만 account_seq 기록. 사용자 환경에 맞게 index·fields를 자유롭게 확장할 수 있습니다.",
|
|
4
4
|
"db_group": "system",
|
|
5
5
|
"hard_delete": true,
|
|
6
6
|
"history_ttl": 0,
|
|
7
7
|
"read_only": true,
|
|
8
8
|
"license_scope": false,
|
|
9
|
+
"compress": true,
|
|
9
10
|
"index": {
|
|
10
11
|
"transaction_id": {
|
|
11
12
|
"comment": "요청 트랜잭션 ID. entity_history 테이블과 JOIN 키",
|
|
@@ -1,19 +1,15 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "
|
|
3
|
-
"description": "
|
|
2
|
+
"name": "goods",
|
|
3
|
+
"description": "goods Entity. 사용자 환경에 맞게 index·fields를 자유롭게 확장할 수 있습니다.",
|
|
4
4
|
"index": {
|
|
5
5
|
"name": {
|
|
6
|
-
"comment": "
|
|
6
|
+
"comment": "상품명",
|
|
7
7
|
"required": true,
|
|
8
8
|
"unique": true
|
|
9
9
|
},
|
|
10
10
|
"category": {
|
|
11
11
|
"comment": "카테고리",
|
|
12
|
-
"type": [
|
|
13
|
-
"전자제품",
|
|
14
|
-
"가구",
|
|
15
|
-
"생활용품"
|
|
16
|
-
]
|
|
12
|
+
"type": ["전자제품", "가구", "생활용품"]
|
|
17
13
|
},
|
|
18
14
|
"price": {
|
|
19
15
|
"comment": "가격"
|
|
@@ -21,13 +17,13 @@
|
|
|
21
17
|
},
|
|
22
18
|
"reset_defaults": [
|
|
23
19
|
{
|
|
24
|
-
"name": "샘플
|
|
20
|
+
"name": "샘플 상품 A",
|
|
25
21
|
"category": "전자제품",
|
|
26
22
|
"price": 10000,
|
|
27
23
|
"stock": 50
|
|
28
24
|
},
|
|
29
25
|
{
|
|
30
|
-
"name": "샘플
|
|
26
|
+
"name": "샘플 상품 B",
|
|
31
27
|
"category": "가구",
|
|
32
28
|
"price": 50000,
|
|
33
29
|
"stock": 20
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
`entities/` 디렉토리에 배치하는 `.json` 설정 파일 예제 모음입니다.
|
|
4
4
|
엔티티 서버의 **모든 주요 기능을 하나씩** 알아볼 수 있도록 주제별로 구성하였습니다.
|
|
5
5
|
|
|
6
|
-
> **전체 레퍼런스**: [docs/
|
|
7
|
-
> **훅 가이드**: [docs/
|
|
6
|
+
> **전체 레퍼런스**: [docs/guides/data/entity-config-guide.md](../../docs/guides/data/entity-config-guide.md)
|
|
7
|
+
> **훅 가이드**: [docs/guides/extensions/hooks.md](../../docs/guides/extensions/hooks.md)
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
@@ -31,6 +31,11 @@ Options:
|
|
|
31
31
|
--export Print shell export format
|
|
32
32
|
--apply Apply values directly to project .env
|
|
33
33
|
|
|
34
|
+
Generated keys:
|
|
35
|
+
ENCRYPTION_KEY Master encryption key (32-byte hex)
|
|
36
|
+
JWT_SECRET JWT signing key (64-byte hex)
|
|
37
|
+
BACKUP_ENCRYPT_KEY Backup file encryption key (32-byte hex)
|
|
38
|
+
|
|
34
39
|
Examples:
|
|
35
40
|
./scripts/generate-env-keys.sh --create
|
|
36
41
|
./scripts/generate-env-keys.sh --export
|
|
@@ -53,6 +58,11 @@ ENCRYPTION_KEY, JWT_SECRET 랜덤 값을 생성합니다.
|
|
|
53
58
|
--export export 형식으로 출력
|
|
54
59
|
--apply 프로젝트 루트 .env 파일에 즉시 반영
|
|
55
60
|
|
|
61
|
+
생성 키:
|
|
62
|
+
ENCRYPTION_KEY 마스터 암호화 키 (32바이트 hex)
|
|
63
|
+
JWT_SECRET JWT 서명 키 (64바이트 hex)
|
|
64
|
+
BACKUP_ENCRYPT_KEY 백업 파일 암호화 키 (32바이트 hex)
|
|
65
|
+
|
|
56
66
|
예제:
|
|
57
67
|
./scripts/generate-env-keys.sh --create
|
|
58
68
|
./scripts/generate-env-keys.sh --export
|
|
@@ -106,17 +116,20 @@ gen_hex() {
|
|
|
106
116
|
|
|
107
117
|
ENCRYPTION_KEY="$(gen_hex 16)"
|
|
108
118
|
JWT_SECRET="$(gen_hex 32)"
|
|
119
|
+
BACKUP_ENCRYPT_KEY="$(gen_hex 16)"
|
|
109
120
|
|
|
110
121
|
if [[ "$OUTPUT_MODE" == "export" ]]; then
|
|
111
122
|
cat <<EOF
|
|
112
123
|
export ENCRYPTION_KEY=$ENCRYPTION_KEY
|
|
113
124
|
export JWT_SECRET=$JWT_SECRET
|
|
125
|
+
export BACKUP_ENCRYPT_KEY=$BACKUP_ENCRYPT_KEY
|
|
114
126
|
EOF
|
|
115
127
|
elif [[ "$OUTPUT_MODE" == "dotenv" ]]; then
|
|
116
128
|
cat <<EOF
|
|
117
129
|
# $( [ "$LANGUAGE" = "en" ] && echo "Copy & paste to .env" || echo ".env에 복사해서 붙여넣기" )
|
|
118
130
|
ENCRYPTION_KEY=$ENCRYPTION_KEY
|
|
119
131
|
JWT_SECRET=$JWT_SECRET
|
|
132
|
+
BACKUP_ENCRYPT_KEY=$BACKUP_ENCRYPT_KEY
|
|
120
133
|
EOF
|
|
121
134
|
else
|
|
122
135
|
# --apply
|
|
@@ -135,14 +148,17 @@ else
|
|
|
135
148
|
|
|
136
149
|
upsert_env_key "ENCRYPTION_KEY" "$ENCRYPTION_KEY"
|
|
137
150
|
upsert_env_key "JWT_SECRET" "$JWT_SECRET"
|
|
151
|
+
upsert_env_key "BACKUP_ENCRYPT_KEY" "$BACKUP_ENCRYPT_KEY"
|
|
138
152
|
|
|
139
153
|
if [ "$LANGUAGE" = "en" ]; then
|
|
140
154
|
echo "✓ Updated: $ENV_FILE"
|
|
141
155
|
echo " - ENCRYPTION_KEY"
|
|
142
156
|
echo " - JWT_SECRET"
|
|
157
|
+
echo " - BACKUP_ENCRYPT_KEY"
|
|
143
158
|
else
|
|
144
159
|
echo "✓ 업데이트 완료: $ENV_FILE"
|
|
145
160
|
echo " - ENCRYPTION_KEY"
|
|
146
161
|
echo " - JWT_SECRET"
|
|
162
|
+
echo " - BACKUP_ENCRYPT_KEY"
|
|
147
163
|
fi
|
|
148
164
|
fi
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<h2 style="margin: 0 0 8px; font-size: 22px; font-weight: 700; color: #1a1a2e;">계정 휴면 예정 안내</h2>
|
|
2
|
+
<p style="margin: 0 0 24px; font-size: 13px; color: #999;">D-${days_left|30}</p>
|
|
3
|
+
<p style="margin: 0 0 20px; font-size: 15px; color: #555; line-height: 1.6;">
|
|
4
|
+
안녕하세요.<br>
|
|
5
|
+
<strong>${email}</strong> 계정이 <strong>${days_left|30}일 후</strong> 장기 미접속으로 인해 휴면 상태로 전환될 예정입니다.
|
|
6
|
+
</p>
|
|
7
|
+
<div style="background-color: #fff8e1; border-left: 4px solid #f59e0b; border-radius: 4px; padding: 16px 20px; margin: 0 0 24px;">
|
|
8
|
+
<p style="margin: 0; font-size: 14px; color: #92400e; line-height: 1.6;">
|
|
9
|
+
휴면 계정은 로그인이 제한되며, 서비스 이용을 위해 별도의 본인 인증이 필요합니다.
|
|
10
|
+
</p>
|
|
11
|
+
</div>
|
|
12
|
+
<p style="margin: 0 0 24px; font-size: 15px; color: #555; line-height: 1.6;">
|
|
13
|
+
지금 로그인하면 휴면 전환을 방지할 수 있습니다.
|
|
14
|
+
</p>
|
|
15
|
+
<div style="text-align: center; margin: 0 0 28px;">
|
|
16
|
+
<a href="${login_url|#}" style="display: inline-block; padding: 14px 40px; background-color: #1a1a2e; color: #ffffff; text-decoration: none; border-radius: 6px; font-size: 15px; font-weight: 600;">지금 로그인하기</a>
|
|
17
|
+
</div>
|
|
18
|
+
<p style="margin: 0; font-size: 13px; color: #aaa; line-height: 1.6;">
|
|
19
|
+
이미 최근에 로그인하셨다면 이 안내는 무시하셔도 됩니다.
|
|
20
|
+
</p>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<h2 style="margin: 0 0 8px; font-size: 22px; font-weight: 700; color: #1a1a2e;">비밀번호 변경 안내</h2>
|
|
2
|
+
<p style="margin: 0 0 24px; font-size: 13px; color: #999;">D-${days_left|14}</p>
|
|
3
|
+
<p style="margin: 0 0 20px; font-size: 15px; color: #555; line-height: 1.6;">
|
|
4
|
+
안녕하세요.<br>
|
|
5
|
+
<strong>${email}</strong> 계정의 비밀번호가 <strong>${days_left|14}일 후</strong> 만료됩니다.
|
|
6
|
+
</p>
|
|
7
|
+
<div style="background-color: #fef2f2; border-left: 4px solid #ef4444; border-radius: 4px; padding: 16px 20px; margin: 0 0 24px;">
|
|
8
|
+
<p style="margin: 0; font-size: 14px; color: #991b1b; line-height: 1.6;">
|
|
9
|
+
만료된 비밀번호로는 로그인 후 즉시 비밀번호 변경이 요구될 수 있습니다.<br>
|
|
10
|
+
정기적인 비밀번호 변경으로 계정을 안전하게 보호하세요.
|
|
11
|
+
</p>
|
|
12
|
+
</div>
|
|
13
|
+
<p style="margin: 0 0 24px; font-size: 15px; color: #555; line-height: 1.6;">
|
|
14
|
+
아래 버튼을 클릭하여 지금 비밀번호를 변경하세요.
|
|
15
|
+
</p>
|
|
16
|
+
<div style="text-align: center; margin: 0 0 28px;">
|
|
17
|
+
<a href="${change_password_url|#}" style="display: inline-block; padding: 14px 40px; background-color: #1a1a2e; color: #ffffff; text-decoration: none; border-radius: 6px; font-size: 15px; font-weight: 600;">비밀번호 변경하기</a>
|
|
18
|
+
</div>
|
|
19
|
+
<p style="margin: 0; font-size: 13px; color: #aaa; line-height: 1.6;">
|
|
20
|
+
이미 비밀번호를 변경하셨다면 이 안내는 무시하셔도 됩니다.
|
|
21
|
+
</p>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<h2 style="margin: 0 0 16px; font-size: 22px; font-weight: 700; color: #1a1a2e;">2단계 인증이 비활성화되었습니다</h2>
|
|
2
|
+
<p style="margin: 0 0 16px; font-size: 15px; color: #555; line-height: 1.6;">
|
|
3
|
+
<strong>${email}</strong> 계정의 2단계 인증(TOTP)이 해제되었습니다.<br>
|
|
4
|
+
앞으로 이메일과 비밀번호만으로 로그인됩니다.
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<div style="margin: 0 0 24px; padding: 20px; background-color: #f8d7da; border-radius: 8px; border-left: 4px solid #dc3545;">
|
|
8
|
+
<p style="margin: 0; font-size: 14px; color: #721c24; line-height: 1.6;">
|
|
9
|
+
<strong>보안 알림:</strong> 2단계 인증이 해제되면 계정 보안 수준이 낮아집니다.<br>
|
|
10
|
+
가능하면 다시 2단계 인증을 설정하는 것을 권장합니다.
|
|
11
|
+
</p>
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
<p style="margin: 0 0 8px; font-size: 14px; color: #888; line-height: 1.6;">
|
|
15
|
+
해제 시각: ${disabled_time}
|
|
16
|
+
</p>
|
|
17
|
+
<p style="margin: 0 0 8px; font-size: 14px; color: #888; line-height: 1.6;">
|
|
18
|
+
해제 방법: ${disabled_by|본인 요청}
|
|
19
|
+
</p>
|
|
20
|
+
|
|
21
|
+
<p style="margin: 0; font-size: 13px; color: #aaa; line-height: 1.6;">
|
|
22
|
+
본인이 해제하지 않은 경우, 즉시 비밀번호를 변경하고 관리자에게 문의하세요.
|
|
23
|
+
</p>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<h2 style="margin: 0 0 16px; font-size: 22px; font-weight: 700; color: #1a1a2e;">복구 코드가 재생성되었습니다</h2>
|
|
2
|
+
<p style="margin: 0 0 16px; font-size: 15px; color: #555; line-height: 1.6;">
|
|
3
|
+
<strong>${email}</strong> 계정의 2단계 인증 복구 코드가 새로 생성되었습니다.<br>
|
|
4
|
+
이전 복구 코드는 모두 <strong>폐기</strong>되었습니다.
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<div style="margin: 0 0 24px; padding: 20px; background-color: #fff3cd; border-radius: 8px; border-left: 4px solid #ffc107;">
|
|
8
|
+
<p style="margin: 0 0 8px; font-size: 14px; font-weight: 700; color: #856404;">⚠ 새 복구 코드를 안전한 곳에 저장하세요</p>
|
|
9
|
+
<p style="margin: 0; font-size: 13px; color: #856404; line-height: 1.5;">
|
|
10
|
+
각 코드는 <strong>한 번만</strong> 사용할 수 있으며, 이 이메일 이후에는 다시 확인할 수 없습니다.
|
|
11
|
+
</p>
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
<div style="text-align: center; margin: 0 0 24px;">
|
|
15
|
+
<table role="presentation" cellpadding="0" cellspacing="0" style="margin: 0 auto;">
|
|
16
|
+
<tr>
|
|
17
|
+
<td style="padding: 16px 32px; background-color: #f0f0f5; border-radius: 8px;">
|
|
18
|
+
<pre style="margin: 0; font-size: 18px; font-weight: 700; color: #1a1a2e; font-family: 'Courier New', monospace; letter-spacing: 2px; line-height: 2;">${recovery_codes}</pre>
|
|
19
|
+
</td>
|
|
20
|
+
</tr>
|
|
21
|
+
</table>
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
<p style="margin: 0 0 16px; font-size: 14px; color: #888; line-height: 1.6;">
|
|
25
|
+
새 복구 코드: <strong>${recovery_count|10}개</strong><br>
|
|
26
|
+
재생성 시각: ${regenerated_time}
|
|
27
|
+
</p>
|
|
28
|
+
|
|
29
|
+
<p style="margin: 0; font-size: 13px; color: #aaa; line-height: 1.6;">
|
|
30
|
+
본인이 요청하지 않은 경우, 계정이 보안 위협에 노출되었을 수 있습니다. 즉시 비밀번호를 변경하고 관리자에게 문의하세요.
|
|
31
|
+
</p>
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<h2 style="margin: 0 0 16px; font-size: 22px; font-weight: 700; color: #1a1a2e;">2단계 인증이 활성화되었습니다</h2>
|
|
2
|
+
<p style="margin: 0 0 16px; font-size: 15px; color: #555; line-height: 1.6;">
|
|
3
|
+
<strong>${email}</strong> 계정에 2단계 인증(TOTP)이 설정되었습니다.<br>
|
|
4
|
+
앞으로 로그인 시 Authenticator 앱의 인증 코드가 필요합니다.
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<div style="margin: 0 0 24px; padding: 20px; background-color: #fff3cd; border-radius: 8px; border-left: 4px solid #ffc107;">
|
|
8
|
+
<p style="margin: 0 0 8px; font-size: 14px; font-weight: 700; color: #856404;">⚠ 복구 코드를 안전한 곳에 저장하세요</p>
|
|
9
|
+
<p style="margin: 0 0 12px; font-size: 13px; color: #856404; line-height: 1.5;">
|
|
10
|
+
Authenticator 앱에 접근할 수 없는 경우 아래 복구 코드로 로그인할 수 있습니다.<br>
|
|
11
|
+
각 코드는 <strong>한 번만</strong> 사용할 수 있으며, 이 이메일 이후에는 다시 확인할 수 없습니다.
|
|
12
|
+
</p>
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
<div style="text-align: center; margin: 0 0 24px;">
|
|
16
|
+
<table role="presentation" cellpadding="0" cellspacing="0" style="margin: 0 auto;">
|
|
17
|
+
<tr>
|
|
18
|
+
<td style="padding: 16px 32px; background-color: #f0f0f5; border-radius: 8px;">
|
|
19
|
+
<pre style="margin: 0; font-size: 18px; font-weight: 700; color: #1a1a2e; font-family: 'Courier New', monospace; letter-spacing: 2px; line-height: 2;">${recovery_codes}</pre>
|
|
20
|
+
</td>
|
|
21
|
+
</tr>
|
|
22
|
+
</table>
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<p style="margin: 0 0 8px; font-size: 14px; color: #888; line-height: 1.6;">
|
|
26
|
+
남은 복구 코드: <strong>${recovery_count|10}개</strong>
|
|
27
|
+
</p>
|
|
28
|
+
<p style="margin: 0 0 16px; font-size: 14px; color: #888; line-height: 1.6;">
|
|
29
|
+
설정 시각: ${enabled_time}
|
|
30
|
+
</p>
|
|
31
|
+
|
|
32
|
+
<div style="margin: 0 0 16px; padding: 16px; background-color: #f8f9fa; border-radius: 6px;">
|
|
33
|
+
<p style="margin: 0; font-size: 13px; color: #666; line-height: 1.6;">
|
|
34
|
+
💡 <strong>권장 사항</strong><br>
|
|
35
|
+
• 복구 코드를 출력하거나 비밀번호 관리자에 저장하세요<br>
|
|
36
|
+
• 복구 코드가 3개 이하로 줄면 재생성하세요<br>
|
|
37
|
+
• 본인이 설정하지 않았다면 즉시 비밀번호를 변경하세요
|
|
38
|
+
</p>
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
<p style="margin: 0; font-size: 13px; color: #aaa; line-height: 1.6;">
|
|
42
|
+
본인이 설정하지 않은 경우, 계정이 보안 위협에 노출되었을 수 있습니다. 즉시 비밀번호를 변경하고 관리자에게 문의하세요.
|
|
43
|
+
</p>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<h2 style="margin: 0 0 16px; font-size: 22px; font-weight: 700; color: #1a1a2e;">이메일 인증</h2>
|
|
2
|
+
<p style="margin: 0 0 24px; font-size: 15px; color: #555; line-height: 1.6;">
|
|
3
|
+
아래 인증 코드를 입력하여 이메일 주소를 인증하세요.
|
|
4
|
+
</p>
|
|
5
|
+
<div style="text-align: center; margin: 0 0 28px;">
|
|
6
|
+
<div style="display: inline-block; padding: 18px 40px; background-color: #f4f4f7; border-radius: 8px; border: 1px solid #e0e0e0;">
|
|
7
|
+
<span style="font-size: 32px; font-weight: 700; letter-spacing: 10px; color: #1a1a2e; font-family: 'Courier New', Courier, monospace;">${code|------}</span>
|
|
8
|
+
</div>
|
|
9
|
+
</div>
|
|
10
|
+
<p style="margin: 0 0 8px; font-size: 14px; color: #888; line-height: 1.6;">
|
|
11
|
+
이 코드는 <strong>${expires_in|5분}</strong> 동안 유효합니다.
|
|
12
|
+
</p>
|
|
13
|
+
<p style="margin: 0 0 8px; font-size: 13px; color: #aaa; line-height: 1.6;">
|
|
14
|
+
인증 대상 이메일: <strong>${email|}</strong>
|
|
15
|
+
</p>
|
|
16
|
+
<p style="margin: 0; font-size: 13px; color: #aaa; line-height: 1.6;">
|
|
17
|
+
본인이 요청하지 않은 경우 이 이메일을 무시하세요.
|
|
18
|
+
</p>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<h2 style="margin: 0 0 16px; font-size: 22px; font-weight: 700; color: #1a1a2e;">임시 비밀번호 안내</h2>
|
|
2
|
+
<p style="margin: 0 0 24px; font-size: 15px; color: #555; line-height: 1.6;">
|
|
3
|
+
관리자에 의해 비밀번호가 초기화되었습니다. 아래 임시 비밀번호로 로그인 후 즉시 비밀번호를 변경하세요.
|
|
4
|
+
</p>
|
|
5
|
+
<div style="text-align: center; margin: 0 0 28px;">
|
|
6
|
+
<div style="display: inline-block; padding: 14px 32px; background-color: #f4f4f7; border-radius: 8px; border: 1px solid #e0e0e0;">
|
|
7
|
+
<span style="font-size: 22px; font-weight: 700; letter-spacing: 4px; color: #1a1a2e; font-family: 'Courier New', Courier, monospace;">${temp_password|}</span>
|
|
8
|
+
</div>
|
|
9
|
+
</div>
|
|
10
|
+
<p style="margin: 0 0 16px; font-size: 14px; color: #d9534f; font-weight: 600; line-height: 1.6;">
|
|
11
|
+
⚠ 보안을 위해 로그인 즉시 비밀번호를 변경하시기 바랍니다.
|
|
12
|
+
</p>
|
|
13
|
+
<p style="margin: 0 0 8px; font-size: 14px; color: #888; line-height: 1.6;">
|
|
14
|
+
이메일: <strong>${email|}</strong>
|
|
15
|
+
</p>
|
|
16
|
+
<p style="margin: 0; font-size: 13px; color: #aaa; line-height: 1.6;">
|
|
17
|
+
본인이 요청하지 않은 경우 즉시 관리자에게 문의하세요.
|
|
18
|
+
</p>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<h2 style="margin: 0 0 16px; font-size: 22px; font-weight: 700; color: #1a1a2e;">비밀번호 재설정</h2>
|
|
2
|
+
<p style="margin: 0 0 24px; font-size: 15px; color: #555; line-height: 1.6;">
|
|
3
|
+
비밀번호 재설정이 요청되었습니다. 아래 버튼을 클릭하여 새 비밀번호를 설정하세요.
|
|
4
|
+
</p>
|
|
5
|
+
<div style="text-align: center; margin: 0 0 24px;">
|
|
6
|
+
<a href="${reset_url|#}" style="display: inline-block; padding: 14px 36px; background-color: #1a1a2e; color: #ffffff; text-decoration: none; border-radius: 6px; font-size: 15px; font-weight: 600;">비밀번호 재설정</a>
|
|
7
|
+
</div>
|
|
8
|
+
<p style="margin: 0 0 8px; font-size: 14px; color: #888; line-height: 1.6;">
|
|
9
|
+
버튼이 작동하지 않으면 아래 링크를 브라우저에 붙여넣으세요:
|
|
10
|
+
</p>
|
|
11
|
+
<p style="margin: 0 0 16px; font-size: 13px; color: #4a90d9; word-break: break-all; line-height: 1.6;">
|
|
12
|
+
${reset_url|https://example.com/reset?token=sample}
|
|
13
|
+
</p>
|
|
14
|
+
<p style="margin: 0 0 8px; font-size: 14px; color: #888; line-height: 1.6;">
|
|
15
|
+
이 링크는 <strong>${expires_in|1시간}</strong> 동안 유효합니다.
|
|
16
|
+
</p>
|
|
17
|
+
<p style="margin: 0; font-size: 13px; color: #aaa; line-height: 1.6;">
|
|
18
|
+
본인이 요청하지 않은 경우 이 이메일을 무시하세요. 비밀번호는 변경되지 않습니다.
|
|
19
|
+
</p>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<h2 style="margin: 0 0 16px; font-size: 22px; font-weight: 700; color: #1a1a2e;">인증 코드</h2>
|
|
2
|
+
<p style="margin: 0 0 24px; font-size: 15px; color: #555; line-height: 1.6;">
|
|
3
|
+
아래 코드를 입력하여 본인 인증을 완료하세요.
|
|
4
|
+
</p>
|
|
5
|
+
<div style="text-align: center; margin: 0 0 24px;">
|
|
6
|
+
<div style="display: inline-block; padding: 16px 40px; background-color: #f0f0f5; border-radius: 8px; letter-spacing: 8px; font-size: 36px; font-weight: 700; color: #1a1a2e; font-family: 'Courier New', monospace;">
|
|
7
|
+
${code|000000}
|
|
8
|
+
</div>
|
|
9
|
+
</div>
|
|
10
|
+
<p style="margin: 0 0 8px; font-size: 14px; color: #888; line-height: 1.6;">
|
|
11
|
+
이 코드는 <strong>${expires_in|10분}</strong> 동안 유효합니다.
|
|
12
|
+
</p>
|
|
13
|
+
<p style="margin: 0; font-size: 13px; color: #aaa; line-height: 1.6;">
|
|
14
|
+
본인이 요청하지 않은 경우 이 이메일을 무시하세요.
|
|
15
|
+
</p>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
```php
|
|
2
|
+
<h2 style="margin: 0 0 16px; font-size: 22px; font-weight: 700; color: #1a1a2e;">이메일 인증</h2>
|
|
3
|
+
<p style="margin: 0 0 24px; font-size: 15px; color: #555; line-height: 1.6;">
|
|
4
|
+
아래 버튼을 클릭하여 이메일 인증을 완료하세요.
|
|
5
|
+
</p>
|
|
6
|
+
<div style="text-align: center; margin: 0 0 24px;">
|
|
7
|
+
<a href="${activation_url}" target="_blank" rel="noopener noreferrer"
|
|
8
|
+
style="display: inline-block; padding: 14px 40px; background-color: #1a1a2e; color: #ffffff; text-decoration: none; border-radius: 8px; font-size: 16px; font-weight: 600; letter-spacing: 0.5px;">
|
|
9
|
+
이메일 인증하기
|
|
10
|
+
</a>
|
|
11
|
+
</div>
|
|
12
|
+
<p style="margin: 0 0 8px; font-size: 14px; color: #888; line-height: 1.6;">
|
|
13
|
+
이 링크는 <strong>${expires_in|10분}</strong> 동안 유효합니다.
|
|
14
|
+
</p>
|
|
15
|
+
<p style="margin: 0 0 16px; font-size: 13px; color: #aaa; line-height: 1.6;">
|
|
16
|
+
버튼이 작동하지 않는 경우 아래 링크를 브라우저에 직접 붙여넣으세요:
|
|
17
|
+
</p>
|
|
18
|
+
<p style="margin: 0; font-size: 12px; color: #999; word-break: break-all; line-height: 1.6;">
|
|
19
|
+
${activation_url}
|
|
20
|
+
</p>
|
|
21
|
+
<p style="margin: 16px 0 0; font-size: 13px; color: #aaa; line-height: 1.6;">
|
|
22
|
+
본인이 요청하지 않은 경우 이 이메일을 무시하세요.
|
|
23
|
+
</p>
|
|
24
|
+
|
|
25
|
+
```
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<h2 style="margin: 0 0 16px; font-size: 22px; font-weight: 700; color: #1a1a2e;">환영합니다, ${name|회원}님!</h2>
|
|
2
|
+
<p style="margin: 0 0 24px; font-size: 15px; color: #555; line-height: 1.6;">
|
|
3
|
+
회원가입이 완료되었습니다. ${app_name|서비스}를 이용해 주셔서 감사합니다.
|
|
4
|
+
</p>
|
|
5
|
+
<div style="text-align: center; margin: 0 0 24px;">
|
|
6
|
+
<a href="${login_url|#}" style="display: inline-block; padding: 14px 36px; background-color: #1a1a2e; color: #ffffff; text-decoration: none; border-radius: 6px; font-size: 15px; font-weight: 600;">시작하기</a>
|
|
7
|
+
</div>
|
|
8
|
+
<div style="padding: 20px; background-color: #f8f8fa; border-radius: 6px; margin: 0 0 24px;">
|
|
9
|
+
<p style="margin: 0 0 8px; font-size: 14px; font-weight: 600; color: #1a1a2e;">계정 정보</p>
|
|
10
|
+
<p style="margin: 0; font-size: 14px; color: #555; line-height: 1.8;">
|
|
11
|
+
이메일: ${email|user@example.com}<br>
|
|
12
|
+
아이디: ${account_id|user001}<br>
|
|
13
|
+
가입일: ${created_at|오늘}
|
|
14
|
+
</p>
|
|
15
|
+
</div>
|
|
16
|
+
<p style="margin: 0; font-size: 13px; color: #aaa; line-height: 1.6;">
|
|
17
|
+
궁금한 점이 있으시면 언제든 문의해 주세요.
|
|
18
|
+
</p>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<h2 style="margin: 0 0 16px; font-size: 22px; font-weight: 700; color: #27ae60;">✅ 백업 완료</h2>
|
|
2
|
+
<p style="margin: 0 0 24px; font-size: 15px; color: #555; line-height: 1.6;">
|
|
3
|
+
데이터 백업이 정상적으로 완료되었습니다.
|
|
4
|
+
</p>
|
|
5
|
+
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="margin: 0 0 24px;">
|
|
6
|
+
<tr>
|
|
7
|
+
<td style="padding: 16px 20px; background-color: #eafaf1; border-radius: 6px; border-left: 4px solid #27ae60;">
|
|
8
|
+
<table role="presentation" width="100%" cellpadding="0" cellspacing="0">
|
|
9
|
+
<tr>
|
|
10
|
+
<td style="padding: 4px 0; font-size: 14px; color: #555; width: 120px; font-weight: 600;">세션 ID</td>
|
|
11
|
+
<td style="padding: 4px 0; font-size: 14px; color: #333;">${session_id|—}</td>
|
|
12
|
+
</tr>
|
|
13
|
+
<tr>
|
|
14
|
+
<td style="padding: 4px 0; font-size: 14px; color: #555; width: 120px; font-weight: 600;">상태</td>
|
|
15
|
+
<td style="padding: 4px 0; font-size: 14px; font-weight: 700; color: #27ae60;">완료</td>
|
|
16
|
+
</tr>
|
|
17
|
+
<tr>
|
|
18
|
+
<td style="padding: 4px 0; font-size: 14px; color: #555; width: 120px; font-weight: 600;">레코드 수</td>
|
|
19
|
+
<td style="padding: 4px 0; font-size: 14px; color: #333;">${total_records|0}건</td>
|
|
20
|
+
</tr>
|
|
21
|
+
<tr>
|
|
22
|
+
<td style="padding: 4px 0; font-size: 14px; color: #555; width: 120px; font-weight: 600;">백업 크기</td>
|
|
23
|
+
<td style="padding: 4px 0; font-size: 14px; color: #333;">${total_size|0 B}</td>
|
|
24
|
+
</tr>
|
|
25
|
+
<tr>
|
|
26
|
+
<td style="padding: 4px 0; font-size: 14px; color: #555; width: 120px; font-weight: 600;">소요 시간</td>
|
|
27
|
+
<td style="padding: 4px 0; font-size: 14px; color: #333;">${elapsed|—}</td>
|
|
28
|
+
</tr>
|
|
29
|
+
</table>
|
|
30
|
+
</td>
|
|
31
|
+
</tr>
|
|
32
|
+
</table>
|
|
33
|
+
<p style="margin: 0; font-size: 13px; color: #aaa; line-height: 1.6;">
|
|
34
|
+
이 알림은 백업 완료 시 자동 발송됩니다.
|
|
35
|
+
</p>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<h2 style="margin: 0 0 16px; font-size: 22px; font-weight: 700; color: #e74c3c;">❌ 백업 실패</h2>
|
|
2
|
+
<p style="margin: 0 0 24px; font-size: 15px; color: #555; line-height: 1.6;">
|
|
3
|
+
데이터 백업에 실패했습니다. 오류 내용을 확인하고 조치해 주세요.
|
|
4
|
+
</p>
|
|
5
|
+
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="margin: 0 0 24px;">
|
|
6
|
+
<tr>
|
|
7
|
+
<td style="padding: 16px 20px; background-color: #fdedec; border-radius: 6px; border-left: 4px solid #e74c3c;">
|
|
8
|
+
<table role="presentation" width="100%" cellpadding="0" cellspacing="0">
|
|
9
|
+
<tr>
|
|
10
|
+
<td style="padding: 4px 0; font-size: 14px; color: #555; width: 120px; font-weight: 600;">세션 ID</td>
|
|
11
|
+
<td style="padding: 4px 0; font-size: 14px; color: #333;">${session_id|—}</td>
|
|
12
|
+
</tr>
|
|
13
|
+
<tr>
|
|
14
|
+
<td style="padding: 4px 0; font-size: 14px; color: #555; width: 120px; font-weight: 600;">상태</td>
|
|
15
|
+
<td style="padding: 4px 0; font-size: 14px; font-weight: 700; color: #e74c3c;">실패</td>
|
|
16
|
+
</tr>
|
|
17
|
+
</table>
|
|
18
|
+
</td>
|
|
19
|
+
</tr>
|
|
20
|
+
</table>
|
|
21
|
+
<div style="margin: 0 0 24px;">
|
|
22
|
+
<p style="margin: 0 0 8px; font-size: 14px; font-weight: 600; color: #1a1a2e;">오류 내용</p>
|
|
23
|
+
<pre style="margin: 0; padding: 12px 16px; background-color: #f8f8fa; border-radius: 6px; font-size: 13px; color: #e74c3c; line-height: 1.6; white-space: pre-wrap; word-break: break-all;">${error_message|알 수 없는 오류}</pre>
|
|
24
|
+
</div>
|
|
25
|
+
<p style="margin: 0; font-size: 13px; color: #aaa; line-height: 1.6;">
|
|
26
|
+
이 알림은 백업 실패 시 자동 발송됩니다.
|
|
27
|
+
</p>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<h2 style="margin: 0 0 16px; font-size: 22px; font-weight: 700; color: #e67e22;">⚠️ 백업 부분 완료</h2>
|
|
2
|
+
<p style="margin: 0 0 24px; font-size: 15px; color: #555; line-height: 1.6;">
|
|
3
|
+
백업이 완료되었으나 일부 엔티티에서 오류가 발생했습니다. 실패한 항목을 확인해 주세요.
|
|
4
|
+
</p>
|
|
5
|
+
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="margin: 0 0 24px;">
|
|
6
|
+
<tr>
|
|
7
|
+
<td style="padding: 16px 20px; background-color: #fef9e7; border-radius: 6px; border-left: 4px solid #e67e22;">
|
|
8
|
+
<table role="presentation" width="100%" cellpadding="0" cellspacing="0">
|
|
9
|
+
<tr>
|
|
10
|
+
<td style="padding: 4px 0; font-size: 14px; color: #555; width: 120px; font-weight: 600;">세션 ID</td>
|
|
11
|
+
<td style="padding: 4px 0; font-size: 14px; color: #333;">${session_id|—}</td>
|
|
12
|
+
</tr>
|
|
13
|
+
<tr>
|
|
14
|
+
<td style="padding: 4px 0; font-size: 14px; color: #555; width: 120px; font-weight: 600;">상태</td>
|
|
15
|
+
<td style="padding: 4px 0; font-size: 14px; font-weight: 700; color: #e67e22;">부분 완료</td>
|
|
16
|
+
</tr>
|
|
17
|
+
<tr>
|
|
18
|
+
<td style="padding: 4px 0; font-size: 14px; color: #555; width: 120px; font-weight: 600;">레코드 수</td>
|
|
19
|
+
<td style="padding: 4px 0; font-size: 14px; color: #333;">${total_records|0}건</td>
|
|
20
|
+
</tr>
|
|
21
|
+
</table>
|
|
22
|
+
</td>
|
|
23
|
+
</tr>
|
|
24
|
+
</table>
|
|
25
|
+
<div style="margin: 0 0 24px;">
|
|
26
|
+
<p style="margin: 0 0 8px; font-size: 14px; font-weight: 600; color: #1a1a2e;">실패한 엔티티</p>
|
|
27
|
+
<pre style="margin: 0; padding: 12px 16px; background-color: #f8f8fa; border-radius: 6px; font-size: 13px; color: #555; line-height: 1.6; white-space: pre-wrap; word-break: break-all;">${failed_entities|없음}</pre>
|
|
28
|
+
</div>
|
|
29
|
+
<p style="margin: 0; font-size: 13px; color: #aaa; line-height: 1.6;">
|
|
30
|
+
이 알림은 백업 결과에 오류가 포함되었을 때 자동 발송됩니다.
|
|
31
|
+
</p>
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="ko">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<!--[if mso]>
|
|
7
|
+
<noscript>
|
|
8
|
+
<xml>
|
|
9
|
+
<o:OfficeDocumentSettings>
|
|
10
|
+
<o:PixelsPerInch>96</o:PixelsPerInch>
|
|
11
|
+
</o:OfficeDocumentSettings>
|
|
12
|
+
</xml>
|
|
13
|
+
</noscript>
|
|
14
|
+
<![endif]-->
|
|
15
|
+
</head>
|
|
16
|
+
<body style="margin: 0; padding: 0; background-color: #f4f4f7; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%;">
|
|
17
|
+
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background-color: #f4f4f7;">
|
|
18
|
+
<tr>
|
|
19
|
+
<td align="center" style="padding: 24px 16px;">
|
|
20
|
+
<table role="presentation" width="600" cellpadding="0" cellspacing="0" style="max-width: 600px; width: 100%; background-color: #ffffff; border-radius: 8px; overflow: hidden; box-shadow: 0 1px 3px rgba(0,0,0,0.08);">
|
|
21
|
+
<!-- Header -->
|
|
22
|
+
<tr>
|
|
23
|
+
<td style="padding: 32px 40px 24px; border-bottom: 1px solid #eaeaea;">
|
|
24
|
+
<span style="font-size: 18px; font-weight: 700; color: #1a1a2e;">${app_name|Entity Server}</span>
|
|
25
|
+
</td>
|
|
26
|
+
</tr>
|
|
27
|
+
<!-- Content -->
|
|
28
|
+
<tr>
|
|
29
|
+
<td style="padding: 32px 40px;">
|
|
30
|
+
${content}
|
|
31
|
+
</td>
|
|
32
|
+
</tr>
|
|
33
|
+
<!-- Footer -->
|
|
34
|
+
<tr>
|
|
35
|
+
<td style="padding: 24px 40px; border-top: 1px solid #eaeaea; background-color: #fafafa;">
|
|
36
|
+
<p style="margin: 0; font-size: 12px; color: #999; line-height: 1.6;">
|
|
37
|
+
이 이메일은 자동 발송되었습니다. 회신하지 마세요.<br>
|
|
38
|
+
© ${company|Entity Server}
|
|
39
|
+
</p>
|
|
40
|
+
</td>
|
|
41
|
+
</tr>
|
|
42
|
+
</table>
|
|
43
|
+
</td>
|
|
44
|
+
</tr>
|
|
45
|
+
</table>
|
|
46
|
+
</body>
|
|
47
|
+
</html>
|